: 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 itExtract MethodswitchStatement to a separate function, and thenMove MethodMove it to the class that requires polymorphism.
  • If you have aswitchIs based on the type code to identify the branch, this can be usedReplace Type Code with SubclassReplace Type Code with State/Strategy pattern
  • Once this inheritance structure is complete, you can use itReplace 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 useReplace Parameter with Explicit Methods
  • If one of your criteria is null, applyIntroduce Null Object (Introduce Null Object)

earnings

  • Improve code organization.



When to ignore

  • If aswitchOperations perform simple actions and there is no need for refactoring.
  • switchOften used by family of factory design patterns (Factory Method patternandAbstract 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 byExtract ClassDistills the temporary fields and all the code that operates on them into a single class. In addition, you can useReplace Method with Method ObjectTo 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, useRename Method (Rename Method)Rename them according to their purpose.
  • usingMove MethodAdd ParameterLet 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 itExtract 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 itReplace Inheritance with DelegationEliminate inheritance.
  • If inheritance is appropriate, remove fields and methods that are not needed in the subclass. usingExtract SuperclassTake 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

  1. Create a new field in the subclass to hold the superclass.
  2. Adjust the subclass function to delegate to the superclass instead;
  3. 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