整理自:https://sourcemaking.com/refactoring/smells
類型一:Bloaters
代碼、方法和類增長到龐大的量。
Long Method
一般來說,長于10行的方法就應(yīng)該考慮是否需要重構(gòu)。如果方法中有些地方需要注釋,就應(yīng)該考慮是否把這些代碼放入新方法內(nèi),即使該代碼只有一行,原因是如果方法名是描述性的,就不需要閱讀進(jìn)行實際操作的源碼。
解決方案:
- Extract method
例如:
void printOwing() {
printBanner();
//print details
System.out.println("name: " + name);
System.out.println("amount: " + getOutstanding());
}
改為
void printOwing() {
printBanner();
printDetails(getOutstanding());
}
void printDetails(double outstanding) {
System.out.println("name: " + name);
System.out.println("amount: " + outstanding);
}
- Replace Temp with Query
例如:
double calculateTotal() {
double basePrice = quantity * itemPrice;
if (basePrice > 1000) {
return basePrice * 0.95;
}
else {
return basePrice * 0.98;
}
}
改為
double calculateTotal() {
if (basePrice() > 1000) {
return basePrice() * 0.95;
}
else {
return basePrice() * 0.98;
}
}
double basePrice() {
return quantity * itemPrice;
}
- Introduce Parameter Object

- Preserve Whole Object
例如:
int low = daysTempRange.getLow();
int high = daysTempRange.getHigh();
boolean withinPlan = plan.withinRange(low, high);
改為
boolean withinPlan = plan.withinRange(daysTempRange);
- Replace Method with Method Object
例如:
class Order {
//...
public double price() {
double primaryBasePrice;
double secondaryBasePrice;
double tertiaryBasePrice;
// long computation.
//...
}
}
改為
class Order {
//...
public double price() {
return new PriceCalculator(this).compute();
}
}
class PriceCalculator {
private double primaryBasePrice;
private double secondaryBasePrice;
private double tertiaryBasePrice;
public PriceCalculator(Order order) {
// copy relevant information from order object.
//...
}
public double compute() {
// long computation.
//...
}
}
- Decompose Conditional
例如:
if (date.before(SUMMER_START) || date.after(SUMMER_END)) {
charge = quantity * winterRate + winterServiceCharge;
}
else {
charge = quantity * summerRate;
}
改為
if (isSummer(date)) {
charge = summerCharge(quantity);
}
else {
charge = winterCharge(quantity);
}
Large Class
一個包含了很多字段/方法/代碼行的類。
解決方案:
- Extract Class
When one class does the work of two, awkwardness results.

- Extract Subclass
A class has features that are used only in certain cases.

- Extract Interface
Multiple clients are using the same part of a class interface. Another case: part of the interface in two classes is the same.

- Duplicate Observed Data
Is domain data stored in classes responsible for the GUI?

Primitive Obsession
用primitive field而不是small object代表簡單事物;用常數(shù)來表示信息等。
解決方案:
- Replace Data Value with Object
A class (or group of classes) contains a data field. The field has its own behavior and associated data.

- Introduce Parameter Object

- Preserve Whole Object
例如:
int low = daysTempRange.getLow();
int high = daysTempRange.getHigh();
boolean withinPlan = plan.withinRange(low, high);
改為
boolean withinPlan = plan.withinRange(daysTempRange);
- Replace Type Code with Class
A class has a field that contains type code. The values of this type are not used in operator conditions and do not affect the behavior of the program.

- Replace Type Code with Subclasses
You have a coded type that directly affects program behavior (values of this field trigger various code in conditionals).

- Replace Array with Object
例如:
String[] row = new String[2];
row[0] = "Liverpool";
row[1] = "15";
改為
Performance row = new Performance();
row.setName("Liverpool");
row.setWins("15");
Long Parameter List
一個方法包含了三個或四個以上的參數(shù)。
解決方案:
- Replace Parameter with Method Call
例如:
int basePrice = quantity * itemPrice;
double seasonDiscount = this.getSeasonalDiscount();
double fees = this.getFees();
double finalPrice = discountedPrice(basePrice, seasonDiscount, fees);
改為
int basePrice = quantity * itemPrice;
double finalPrice = discountedPrice(basePrice);
- Preserve Whole Object
例如:
int low = daysTempRange.getLow();
int high = daysTempRange.getHigh();
boolean withinPlan = plan.withinRange(low, high);
改為
boolean withinPlan = plan.withinRange(daysTempRange);
- Introduce Parameter Object

Data Clumps
不同部分的代碼都包含了相同的一堆變量(即數(shù)據(jù)塊)。如果要確定某些數(shù)據(jù)是否為數(shù)據(jù)塊,只需刪除其中一個數(shù)據(jù)值,然后查看其他值是否仍然有意義。如果不是,表明這組變量應(yīng)該組合成一個對象。
- Extract Class
When one class does the work of two, awkwardness results.

- Introduce Parameter Object

- Preserve Whole Object
例如:
int low = daysTempRange.getLow();
int high = daysTempRange.getHigh();
boolean withinPlan = plan.withinRange(low, high);
改為
boolean withinPlan = plan.withinRange(daysTempRange);
類型二:Object-Orientation Abusers
對OOP法則的不完整或是不正確的運用。
Switch Statements
存在復(fù)雜的switch操作或是一系列if語句。
解決方案:
- Extract method
例如:
void printOwing() {
printBanner();
//print details
System.out.println("name: " + name);
System.out.println("amount: " + getOutstanding());
}
改為
void printOwing() {
printBanner();
printDetails(getOutstanding());
}
void printDetails(double outstanding) {
System.out.println("name: " + name);
System.out.println("amount: " + outstanding);
}
- Move Method
A method is used more in another class than in its own class.

- Replace Type Code with Subclasses
You have a coded type that directly affects program behavior (values of this field trigger various code in conditionals).

- Replace Conditional with Polymorphism
例如:
class Bird {
//...
double getSpeed() {
switch (type) {
case EUROPEAN:
return getBaseSpeed();
case AFRICAN:
return getBaseSpeed() - getLoadFactor() * numberOfCoconuts;
case NORWEGIAN_BLUE:
return (isNailed) ? 0 : getBaseSpeed(voltage);
}
throw new RuntimeException("Should be unreachable");
}
}
改為
abstract class Bird {
//...
abstract double getSpeed();
}
class European extends Bird {
double getSpeed() {
return getBaseSpeed();
}
}
class African extends Bird {
double getSpeed() {
return getBaseSpeed() - getLoadFactor() * numberOfCoconuts;
}
}
class NorwegianBlue extends Bird {
double getSpeed() {
return (isNailed) ? 0 : getBaseSpeed(voltage);
}
}
// Somewhere in client code
speed = bird.getSpeed();
- Replace Parameter with Explicit Methods
例如:
void setValue(String name, int value) {
if (name.equals("height")) {
height = value;
return;
}
if (name.equals("width")) {
width = value;
return;
}
Assert.shouldNeverReachHere();
}
改為
void setHeight(int arg) {
height = arg;
}
void setWidth(int arg) {
width = arg;
}
- Introduce Null Object
例如:
if (customer == null) {
plan = BillingPlan.basic();
}
else {
plan = customer.getPlan();
}
改為
class NullCustomer extends Customer {
boolean isNull() {
return true;
}
Plan getPlan() {
return new NullPlan();
}
// Some other NULL functionality.
}
// Replace null values with Null-object.
customer = (order.customer != null) ?
order.customer : new NullCustomer();
// Use Null-object as if it's normal subclass.
plan = customer.getPlan();
Temporary Field
字段只在某些情況下被賦值,而在剩下的情況下,都不會被賦值。
解決方案:
- Extract Class
When one class does the work of two, awkwardness results.

- Replace Method with Method Object
例如:
class Order {
//...
public double price() {
double primaryBasePrice;
double secondaryBasePrice;
double tertiaryBasePrice;
// long computation.
//...
}
}
改為
class Order {
//...
public double price() {
return new PriceCalculator(this).compute();
}
}
class PriceCalculator {
private double primaryBasePrice;
private double secondaryBasePrice;
private double tertiaryBasePrice;
public PriceCalculator(Order order) {
// copy relevant information from order object.
//...
}
public double compute() {
// long computation.
//...
}
}
- Introduce Null Object
例如:
if (customer == null) {
plan = BillingPlan.basic();
}
else {
plan = customer.getPlan();
}
改為
class NullCustomer extends Customer {
boolean isNull() {
return true;
}
Plan getPlan() {
return new NullPlan();
}
// Some other NULL functionality.
}
// Replace null values with Null-object.
customer = (order.customer != null) ?
order.customer : new NullCustomer();
// Use Null-object as if it's normal subclass.
plan = customer.getPlan();
Refused Bequest
如果子類僅使用從其父項繼承的某些方法和屬性,則說明使用這種層次結(jié)構(gòu)是不合適的。
解決方案:
- Replace Inheritance with Delegation
You have a subclass that uses only a portion of the methods of its superclass (or it’s not possible to inherit superclass data).

- Extract Superclass
You have two classes with common fields and methods.

Alternative Classes with Different Interfaces
兩個類的功能幾乎一樣只是方法名不同。
- Rename Method
The name of a method does not explain what the method does.

- Move Method
A method is used more in another class than in its own class.

- Add Parameter
A method does not have enough data to perform certain actions.

- Parameterize Method
Multiple methods perform similar actions that are different only in their internal values, numbers or operations.

- Extract Superclass
You have two classes with common fields and methods.

類型三:Change Preventers
如果你需要在代碼中的某個位置更改某些內(nèi)容,則必須在其他位置進(jìn)行許多更改。
Divergent Change
當(dāng)對某個類進(jìn)行更改時,你發(fā)現(xiàn)自己必須更改許多不相關(guān)的方法。
解決方案:
- Extract Class
When one class does the work of two, awkwardness results.

- Extract Superclass
You have two classes with common fields and methods.

- Extract Subclass
A class has features that are used only in certain cases.

Shotgun Surgery
進(jìn)行任何修改都需要對許多不同的類進(jìn)行許多小的更改。
解決方案:
- Move Method
A method is used more in another class than in its own class.

- Move Field
A field is used more in another class than in its own class.

- Inline Class
A class does almost nothing and is not responsible for anything, and no additional responsibilities are planned for it.

Parallel Inheritance Hierarchies
每當(dāng)為類創(chuàng)建子類時,你發(fā)現(xiàn)自己需要為另一個類創(chuàng)建子類。
解決方案:
- Move Method
A method is used more in another class than in its own class.

- Move Field
A field is used more in another class than in its own class.

類型四:Dispensables
指毫無意義或是不必要的東西,將其移除會使代碼更清晰,更有效,更容易理解。
Comments
方法里面充滿了解釋性注釋。
解決方案:
- Extract Variable
例如:
void renderBanner() {
if ((platform.toUpperCase().indexOf("MAC") > -1) &&
(browser.toUpperCase().indexOf("IE") > -1) &&
wasInitialized() && resize > 0 )
{
// do something
}
}
改為
void renderBanner() {
final boolean isMacOs = platform.toUpperCase().indexOf("MAC") > -1;
final boolean isIE = browser.toUpperCase().indexOf("IE") > -1;
final boolean wasResized = resize > 0;
if (isMacOs && isIE && wasInitialized() && wasResized) {
// do something
}
}
解決方案:
- Extract method
例如:
void printOwing() {
printBanner();
//print details
System.out.println("name: " + name);
System.out.println("amount: " + getOutstanding());
}
改為
void printOwing() {
printBanner();
printDetails(getOutstanding());
}
void printDetails(double outstanding) {
System.out.println("name: " + name);
System.out.println("amount: " + outstanding);
}
- Rename Method
The name of a method does not explain what the method does.

- Introduce Assertion
例如:
double getExpenseLimit() {
// should have either expense limit or a primary project
return (expenseLimit != NULL_EXPENSE) ?
expenseLimit:
primaryProject.getMemberExpenseLimit();
}
改為
double getExpenseLimit() {
Assert.isTrue(expenseLimit != NULL_EXPENSE || primaryProject != null);
return (expenseLimit != NULL_EXPENSE) ?
expenseLimit:
primaryProject.getMemberExpenseLimit();
}
Duplicate Code
兩個或多個代碼片段看起來幾乎相同。
解決方案:
- Extract method
例如:
void printOwing() {
printBanner();
//print details
System.out.println("name: " + name);
System.out.println("amount: " + getOutstanding());
}
改為
void printOwing() {
printBanner();
printDetails(getOutstanding());
}
void printDetails(double outstanding) {
System.out.println("name: " + name);
System.out.println("amount: " + outstanding);
}
- Pull Up Field
Two classes have the same field.

- Pull Up Constructor Body
例如:
class Manager extends Employee {
public Manager(String name, String id, int grade) {
this.name = name;
this.id = id;
this.grade = grade;
}
//...
}
改為
class Manager extends Employee {
public Manager(String name, String id, int grade) {
super(name, id);
this.grade = grade;
}
//...
}
- Form Template Method
Your subclasses implement algorithms that contain similar steps in the same order.

- Substitute Algorithm
例如:
String foundPerson(String[] people){
for (int i = 0; i < people.length; i++) {
if (people[i].equals("Don")){
return "Don";
}
if (people[i].equals("John")){
return "John";
}
if (people[i].equals("Kent")){
return "Kent";
}
}
return "";
}
改為
String foundPerson(String[] people){
List candidates =
Arrays.asList(new String[] {"Don", "John", "Kent"});
for (int i=0; i < people.length; i++) {
if (candidates.contains(people[i])) {
return people[i];
}
}
return "";
}
- Extract Superclass
You have two classes with common fields and methods.

- Extract Class
When one class does the work of two, awkwardness results.

- Consolidate Conditional Expression
You have multiple conditionals that lead to the same result or action.
例如:
double disabilityAmount() {
if (seniority < 2) {
return 0;
}
if (monthsDisabled > 12) {
return 0;
}
if (isPartTime) {
return 0;
}
// compute the disability amount
//...
}
改為
double disabilityAmount() {
if (isNotEligableForDisability()) {
return 0;
}
// compute the disability amount
//...
}
解決方案:
- Extract method
例如:
void printOwing() {
printBanner();
//print details
System.out.println("name: " + name);
System.out.println("amount: " + getOutstanding());
}
改為
void printOwing() {
printBanner();
printDetails(getOutstanding());
}
void printDetails(double outstanding) {
System.out.println("name: " + name);
System.out.println("amount: " + outstanding);
}
- Consolidate Duplicate Conditional Fragments
例如:
if (isSpecialDeal()) {
total = price * 0.95;
send();
}
else {
total = price * 0.98;
send();
}
改為
if (isSpecialDeal()) {
total = price * 0.95;
}
else {
total = price * 0.98;
}
send();
Lazy Class
如果一個類的幾乎不會被用上,就應(yīng)該被刪除。
解決方案:
- Inline Class
A class does almost nothing and is not responsible for anything, and no additional responsibilities are planned for it.

- Collapse Hierarchy
You have a class hierarchy in which a subclass is practically the same as its superclass.

Data Class
數(shù)據(jù)類是指僅包含用于訪問它們的字段和粗略方法的類(getter和setter)。這些只是其他類使用的數(shù)據(jù)的容器。這些類不包含任何其他功能,也不能獨立操作它們擁有的數(shù)據(jù)。
解決方案:
- Encapsulate Field
例如:
class Person {
public String name;
}
改為
class Person {
private String name;
public String getName() {
return name;
}
public void setName(String arg) {
name = arg;
}
}
- Encapsulate Collection
A class contains a collection field and a simple getter and setter for working with the collection.

- Move Method
A method is used more in another class than in its own class.

- Extract method
例如:
void printOwing() {
printBanner();
//print details
System.out.println("name: " + name);
System.out.println("amount: " + getOutstanding());
}
改為
void printOwing() {
printBanner();
printDetails(getOutstanding());
}
void printDetails(double outstanding) {
System.out.println("name: " + name);
System.out.println("amount: " + outstanding);
}
- Remove Setting Method
The value of a field should be set only when it is created, and not change at any time after that.

- Hide Method
A method is not used by other classes or is used only inside its own class hierarchy.

Dead Code
不再被使用的變量,參數(shù),字段,方法或類。
解決方案:
使用好的IDE,通過IDE標(biāo)注找到?jīng)]被使用的變量,參數(shù),字段,方法或類。
Inline Class
A class does almost nothing and is not responsible for anything, and no additional responsibilities are planned for it.

- Collapse Hierarchy
You have a class hierarchy in which a subclass is practically the same as its superclass.

- Remove Parameter
A parameter is not used in the body of a method.

Couplers
類與類之間過度耦合。
Feature Envy
方法訪問另一個對象的數(shù)據(jù)多于訪問自己的數(shù)據(jù)。
解決方案:
- Move Method
A method is used more in another class than in its own class.

- Extract method
例如:
void printOwing() {
printBanner();
//print details
System.out.println("name: " + name);
System.out.println("amount: " + getOutstanding());
}
改為
void printOwing() {
printBanner();
printDetails(getOutstanding());
}
void printDetails(double outstanding) {
System.out.println("name: " + name);
System.out.println("amount: " + outstanding);
}
Inappropriate Intimacy
一個類使用另一個類的內(nèi)部字段和方法。
解決方案:
- Move Method
A method is used more in another class than in its own class.

- Move Field
A field is used more in another class than in its own class.

- Extract Class
When one class does the work of two, awkwardness results.

- Hide Delegate
The client gets object B from a field or method of object А. Then the client calls a method of object B.

- Change Bidirectional Association to Unidirectional
You have a bidirectional association between classes, but one of the classes does not use the other’s features.

- Replace Delegation with Inheritance
A class contains many simple methods that delegate to all methods of another class.

Message Chains
在代碼中你看到類似于這樣的一系列的方法調(diào)用: $a->b()->c()->d()。
解決方案:
- Hide Delegate
The client gets object B from a field or method of object А. Then the client calls a method of object B.

- Extract method
例如:
void printOwing() {
printBanner();
//print details
System.out.println("name: " + name);
System.out.println("amount: " + getOutstanding());
}
改為
void printOwing() {
printBanner();
printDetails(getOutstanding());
}
void printDetails(double outstanding) {
System.out.println("name: " + name);
System.out.println("amount: " + outstanding);
}
- Move Method
A method is used more in another class than in its own class.

Middle Man
如果一個類只執(zhí)行將工作委托給另一個類,那么它就沒有存在的必要。
解決方案:
- Remove Middle Man
A class has too many methods that simply delegate to other objects.
