: Notebook: This article has been filed to: “blog”
Translated from: https://sourcemaking.com/refactoring/smells/oo-abusers
The bad taste of object-orientation Abusers means code that partially or completely violates the principles of object-oriented programming.
The Switch statement
Switch Statements
You have a complex switch statement or an if sequence statement.
Question why
One of the most obvious features of object-oriented programs is the lack of switch and case statements. Essentially, the problem with switch statements is repetition (and the same goes for if sequences). You will often find switch statements scattered in different locations. If you want to add a new case clause to it, you must find all the switch statements and modify them. The concept of polymorphism in object orientation provides an elegant solution for this.
Most of the time, as soon as you see a switch statement, you should consider replacing it with polymorphism.
The solution
- The question is where do polymorphisms occur? The switch statement is often selected based on the type code. You want “functions or classes associated with this type code”, so use it
Extract Method
将switch
Statement to a separate function, and thenMove Method
Move it to the class that requires polymorphism. - If you have a
switch
Is based on the type code to identify the branch, this can be usedReplace Type Code with Subclass
或Replace Type Code with State/Strategy pattern
。 - Once this inheritance structure is complete, you can use it
Replace Conditional with Polymorphism
. - If there are not many conditional branches and they call the same function with different arguments, polymorphism is not necessary. In this case, you can use
Replace Parameter with Explicit Methods
。 - If one of your criteria is null, apply
Introduce Null Object (Introduce Null Object)
。
earnings
- Improve code organization.
When to ignore
- If a
switch
Operations perform simple actions and there is no need for refactoring. switch
Often used by family of factory design patterns (Factory Method pattern
andAbstract Factory pattern
), in which case refactoring is not necessary.
Reconstruction Method description
Extract Method
The problem
You have a piece of code that you can put together.
void printOwing(a) {
printBanner();
//print details
System.out.println("name: " + name);
System.out.println("amount: " + getOutstanding());
}
Copy the code
To solve
Move this code into a new function, replacing the old code with a call to the function.
void printOwing(a) {
printBanner();
printDetails(getOutstanding());
}
void printDetails(double outstanding) {
System.out.println("name: " + name);
System.out.println("amount: " + outstanding);
}
Copy the code
Move Method
The problem
There is a function in your program that communicates more with a class other than the one it resides in: it calls it, or it is called by it.
To solve
Create a new function with similar behavior in the class that this function refers to most often. Turn the old function into a simple delegate function, or remove the old function entirely.
Replace Type Code with Subclass
The problem
You have an immutable type code that affects the behavior of the class.
To solve
Replace this type code with subclasses.
Replace Type Code with State/Strategy pattern
The problem
You have a type code that affects the behavior of the class, but you can’t eliminate it by inheritance.
To solve
Replace type codes with state objects.
Replace Conditional with Polymorphism
The problem
You have a conditional expression that chooses different behavior depending on the type of object.
class Bird {
/ /...
double getSpeed(a) {
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"); }}Copy the code
To solve
Each branch of the conditional expression is put into an overwrite function within a subclass, and the original function is declared abstract.
abstract class Bird {
/ /...
abstract double getSpeed(a);
}
class European extends Bird {
double getSpeed(a) {
returngetBaseSpeed(); }}class African extends Bird {
double getSpeed(a) {
returngetBaseSpeed() - getLoadFactor() * numberOfCoconuts; }}class NorwegianBlue extends Bird {
double getSpeed(a) {
return (isNailed) ? 0: getBaseSpeed(voltage); }}// Somewhere in client code
speed = bird.getSpeed();
Copy the code
Replace Parameter with Explicit Methods
The problem
You have a function that behaves differently depending entirely on the parameter value.
void setValue(String name, int value) {
if (name.equals("height")) {
height = value;
return;
}
if (name.equals("width")) {
width = value;
return;
}
Assert.shouldNeverReachHere();
}
Copy the code
To solve
Create a separate function for each possible value of the parameter.
void setHeight(int arg) {
height = arg;
}
void setWidth(int arg) {
width = arg;
}
Copy the code
Introduce Null Object (Introduce Null Object)
The problem
You need to double check if an object is null.
if (customer == null) {
plan = BillingPlan.basic();
}
else {
plan = customer.getPlan();
}
Copy the code
To solve
Replace null values with null objects.
class NullCustomer extends Customer {
Plan getPlan(a) {
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();
Copy the code
Temporary field
Temporary fields have values that are meaningful only in a certain context; outside that context, they are nothing.
Question why
Sometimes you’ll see objects where an instance variable is set only for a particular case. This kind of code is hard to understand, because you usually think that an object needs all of its variables all the time. It can drive you crazy trying to guess why you set a variable when it’s not in use. Typically, temporary fields are created when an algorithm needs a lot of input. Therefore, to avoid the function having too many arguments, the programmer decides to create temporary fields for this data in the class. These temporary fields are used only in the algorithm and are otherwise useless. This code is hard to understand. You expect to see the data for the object fields, but for some reason, they are always empty.
The solution
- Can be achieved by
Extract Class
Distills the temporary fields and all the code that operates on them into a single class. In addition, you can useReplace Method with Method Object
To do the same thing. Introduce Null Object (Introduce Null Object)
Create a null object if the variable is invalid to avoid writing conditional expressions.
earnings
- Better code clarity and organization.
Reconstruction Method description
Extract Class
The problem
A class does more than one thing.
To solve
Create a new class and move the relevant fields and functions from the old class to the new class.
Replace Method with Method Object
The problem
You have a function that is so long that its local variables are so intertwined that you cannot apply the Extract Method.
class Order {
/ /...
public double price(a) {
double primaryBasePrice;
double secondaryBasePrice;
double tertiaryBasePrice;
// long computation.
/ /...}}Copy the code
To solve
Move the function into a separate class so that local variables become fields of that class. You can then split the function into multiple functions within the class.
class Order {
/ /...
public double price(a) {
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(a) {
// long computation.
/ /...}}Copy the code
Introduce Null Object (Introduce Null Object)
The problem
You need to double check if an object is null.
if (customer == null) {
plan = BillingPlan.basic();
}
else {
plan = customer.getPlan();
}
Copy the code
To solve
Replace null values with null objects.
class NullCustomer extends Customer {
Plan getPlan(a) {
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();
Copy the code
Similar classes
Alternative Classes with Different Interfaces
Two classes have different functions doing the same thing.
Question why
This is often the case because the programmer who created the class did not know that a class that implements this functionality already existed.
The solution
- If two functions do the same thing but have different signatures, use
Rename Method (Rename Method)
Rename them according to their purpose. - using
Move Method
、Add Parameter
和Let the function carry parameters (Parameterize Method)
To make the method name consistent with the implementation. - If only part of the functionality of the two classes is repetitive, try using it
Extract Superclass
. In this case, the existing class becomes the superclass. - When you finally select and apply a method of refactoring, you may be able to remove one of the classes.
earnings
- Eliminate unnecessary duplication of code, the code is slimmed down.
- The code is easier to read (no more guessing why you want two classes that do the same thing).
When to ignore
- Sometimes merging classes is impossible, or so difficult that it doesn’t make sense. For example, two functionally similar classes exist in different Lib libraries.
Reconstruction Method description
Rename Method (Rename Method)
The problem
The name of the function does not properly reveal the purpose of the function.
class Person {
public String getsnm(a);
}
Copy the code
To solve
Change the function name.
class Person {
public String getSecondName(a);
}
Copy the code
Move Method
The problem
There is a function in your program that communicates more with a class other than the one it resides in: it calls it, or it is called by it.
To solve
Create a new function with similar behavior in the class that this function refers to most often. Turn the old function into a simple delegate function, or remove the old function entirely.
Add Parameter
Problem A function needs more information from the calling side.
class Customer {
public Contact getContact(a);
}
Copy the code
The solution is to add an object function to this function, allowing the object to carry the information needed for the function.
class Customer {
public Contact getContact(Date date);
}
Copy the code
Let the function carry parameters (Parameterize Method)
The problem
Several functions do similar work, but contain different values in the function ontology.
To solve
Create a single function that expresses different values as parameters.
Extract Superclass
The problem
The two classes have similar properties.
To solve
Create a superclass for both classes and move the same features to the superclass.
A rejected gift
A Refused Bequest
A child class uses only some of the methods and properties of its parent class. Other gifts from the parent class become a liability.
Question why
Some people create subclasses simply because they want to reuse part of the code in a superclass. But in reality, superclasses and subclasses are completely different.
The solution
- If inheritance doesn’t make sense and there’s really nothing in common between the subclass and the superclass, use it
Replace Inheritance with Delegation
Eliminate inheritance. - If inheritance is appropriate, remove fields and methods that are not needed in the subclass. using
Extract Superclass
Take all the fields and functions from the superclass that are useful to the subclass, put them into a new superclass, and let both classes inherit from it.
earnings
- Improve clarity and organization of your code.
Reconstruction Method description
Replace Inheritance with Delegation
The problem
A subclass uses only a portion of the superclass interface, or does not need inherited data at all.
To solve
- Create a new field in the subclass to hold the superclass.
- Adjust the subclass function to delegate to the superclass instead;
- Then remove the inheritance relationship between the two.
Extract Superclass
The problem
The two classes have similar properties.
To solve
Create a superclass for both classes and move the same features to the superclass.
Further reading
- Bad taste and refactoring of code
- Code bloat with bad taste
- Code with a bad taste for object oriented abuse
- The bad taste of code is a barrier to change
- Code that smells bad is not necessary
- The coupling of code bad smells
The resources
- Refactoring – Improving the design of existing code – by Martin Fowler
- https://sourcemaking.com/refactoring