: Notebook: This article is filed under “blog”

Translate: sourcemaking.com/refactoring…

This group of bad bouts means that groups of classes, functions, or fields in code are not properly organized, they are simply piled up. This type of problem is usually not obvious in the early days of the code, but accumulates as the code grows in size (especially if no one is trying to root them out).

Basic type paranoia

Primitive Obsession

  • Use primitive types instead of small objects for simple tasks (such as currency, ranges, telephone number strings, and so on).
  • Encode information using constants (such as a constant to reference administrator permissions)USER_ADMIN_ROLE = 1).
  • Use string constants as field names in arrays.



Question why

Like most other bad smells, base type paranoia was born when classes were first built. You might start with a few fields, but as you represent more and more features, you get more and more basic data type fields.

Primitive types are often used to represent the type of a model. You have a set of numbers or strings that represent an entity.

Another scenario: in the simulation scenario, a large number of string constants are used to index arrays.

The solution



Most programming languages support both basic data types and structural types (classes, constructs, and so on). Structural types allow programmers to organize basic data types to represent a model of something.

Basic data types can be thought of as building blocks of organization types. Once the number of basic data types is scaled up, organizing them together makes it easier to manage the data.

  • If you have a large number of basic datatype fields, it is possible to organize some of the fields that are logically related into a class. Further, the methods associated with this data are moved into the class as well. To achieve this goal, tryReplace Type Code with Class
  • Can be used if the value of the base datatype field is the parameter for the methodIntroduce Parameter ObjectPreserve the Whole Object
  • This can be used if the data value you want to replace is a type code that does not affect behaviorReplace Type Code with ClassI’m going to replace it. If you have a conditional expression associated with a type code, use itReplace Type Code with SubclassReplace Type Code with State/StrategyTo be processed.
  • Use this if you find yourself picking data from an arrayReplace Array with Object

earnings

  • The code is much more flexible thanks to the use of objects instead of primitive data types.
  • Code becomes more readable and more organized. Special data can be manipulated centrally instead of dispersing as before. No more guessing about what these strange constants mean and why they’re in the array.
  • It’s easier to find duplicate code.



Reconstruction method Description

Replace Type Code with Class

The problem

There is a numeric type code in the class, but it does not affect the behavior of the class.



To solve

Replace the numeric type code with a new class.



Introduce Parameter Object

The problem

Certain parameters naturally come together.



To solve

Replace these parameters with an object.


Preserve the Whole Object

The problem

You take values from an object and use them as arguments to a function call.

int low = daysTempRange.getLow();
int high = daysTempRange.getHigh();
boolean withinPlan = plan.withinRange(low, high);
Copy the code

To solve

Pass the entire object instead.

boolean withinPlan = plan.withinRange(daysTempRange);
Copy the code

Replace Type Code with Subclass

The problem

You have an immutable type code that affects the behavior of the class.



To solve

Replace the type code with a subclass.



Replace Type Code with State/Strategy

The problem

You have a type code that affects the behavior of the class, but you can’t eliminate it through inheritance.



To solve

Replace type codes with state objects.



Replace Array with Object

The problem

You have an array where each element represents something different.

String[] row = new String[3];
row[0] = "Liverpool";
row[1] = "15";
Copy the code

To solve

Replace an array with an object. Each element in the array is represented by a field.

Performance row = new Performance();
row.setName("Liverpool");
row.setWins("15");
Copy the code

Data mud pie

4. Data Clumps

Sometimes, different parts of the code contain the same set of variables (for example, parameters to connect to a database). These bundled data should have their own objects.



Question why

Often, data blobs arise because of poor programming structures or “copy-and-paste programming.”

A good way to tell if it’s a data blob: Delete one of the many items. Does that make other data meaningless? If they no longer make sense, this is a clear signal that you should create a new object for them.

The solution

  • First find out where these data appear in the form of fields and apply themExtract ClassRefine them into a separate object.
  • If the data blob appears in the parameter column of the function, applyIntroduce Parameter ObjectOrganize them into a class.
  • If part of the data blob appears in another function, consider applying itPreserve the Whole ObjectPass the entire data object into the function.
  • Take a look at the code that uses these fields and maybe move them into a data class is a good idea.

earnings

  • Improve code readability and organization. Operations on special data can be processed centrally rather than dispersively as before.
  • Reduce code.



When to ignore

  • Sometimes passing the entire object as an argument to a function for some of the data in the object can create undesirable dependencies between the two classes, in which case the entire object may not be passed.

Reconstruction method Description

Extract Class

The problem

A class does more than one thing.



To solve

Create a new class and move related fields and functions from the old class to the new class.



Introduce Parameter Object

The problem

Certain parameters naturally come together.



To solve

Replace these parameters with an object.



Preserve the Whole Object

The problem

You take values from an object and use them as arguments to a function call.

int low = daysTempRange.getLow();
int high = daysTempRange.getHigh();
boolean withinPlan = plan.withinRange(low, high);
Copy the code

To solve

Pass the entire object instead.

boolean withinPlan = plan.withinRange(daysTempRange);
Copy the code

Too much class

Large classes

A class with too many fields, functions, lines of code.



Question why

Classes usually start small but swell as the program grows.

Similar to overly long functions, programmers often find it easier to add new features to an existing class than to create a new one.

The solution

There is an important principle in design patterns: the single responsibility principle. A class should be given only one responsibility. If it takes on too much responsibility, consider reducing it.



  • This can be used if part of the behavior in a large category can be distilled into a separate componentExtract Class.
  • This can be used if part of the behavior in a large category can be implemented in different ways or used in special scenariosExtract Subclass.
  • This can be used if it is necessary to provide a set of actions and behaviors for the clientExtract Interface.
  • If your large class is a GUI class, you may need to move the data and behavior into a separate domain object. You may want to keep duplicate data on both sides and keep them in sync.Duplicate Observed DataI can tell you what to do.

earnings

  • Refactoring a class that is too large saves the programmer from having to remember a large number of attributes in a class.
  • In most cases, splitting classes that are too large avoids duplication of code and functionality.



Reconstruction method Description

Extract Class

The problem

A class does more than one thing.



To solve

Create a new class and move related fields and functions from the old class to the new class.



Extract Subclass

The problem

There are features in a class that are only used in specific scenarios.



To solve

Create a subclass and put features in it for a particular scenario.



Extract Interface

The problem

Multiple clients use partially the same function in a class. Another scenario is that some functions in two classes are the same.



To solve

Move the same partial function to the interface.



Duplicate Observed Data

The problem

If the data stored in the class is responsible for the GUI.



To solve

A better approach is to put the data responsible for the GUI into a separate class to ensure the connection and synchronization between the GUI data and the domain classes.



Too long to function

Long Method

A function contains too many lines of code. As a general rule, when any function is longer than 10 lines, you can think about whether it’s too long. In principle, the number of lines in a function should not exceed 100.



Cause of the problem

Often, creating a new function is more difficult than adding functionality to an existing one. Most people think, “I’m adding two lines of code, so creating a new function is a storm in a teacup.” So, Zhang SAN plus two lines, Li Si plus two lines, Wang Wu plus two lines… The function gets bigger and bigger, and eventually it’s like a pot of paste, and no one can really understand it anymore. So people are even more afraid to touch the function, only to add code in a vicious cycle. So, if you see a function that’s more than 200 lines long, it’s usually cobbled together by multiple programmers.

To solve the function

A good tip: Look for comments. There are several reasons to add comments: the code logic is obscure or complex; This code is functionally independent; Special treatment. A comment at the front of the code reminds you that you can replace the code with a function, and that you can name the function based on the comment. If the function has a name that describes it properly, you don’t need to look at how the internal code is actually implemented. Even if it’s a single line of code, if it needs to be commented out, it’s worth distilling it into a separate function.



  • To slim down a function, useExtract Method.
  • Can be used if local variables and parameters interfere with the refining functionReplace Temp with Query.Introduce Parameter ObjectPreserve the Whole Object
  • If the first two do not help, you can passReplace Method with Method ObjectTry to move the entire function into a separate object.
  • Conditional expressions and loops are also often signals for refinement. For conditional expressions, you can useDecompose Conditional expressions. As for loops, they should be usedExtract MethodExtract loops and the code inside them into separate functions.

earnings

  • In all types of object-oriented code, classes with shorter and sharper functions tend to have longer lifespans. The longer a function is, the harder it is to understand and maintain.
  • In addition, excessively long functions often contain repetitive code that is hard to find.



performance

Does increasing the number of functions affect performance, as many claim? In almost the vast majority of cases, the effect is negligible, so don’t worry. In addition, now that you have clear and readable code, it will be easier to find really effective functions to reorganize your code and improve performance when you need them.

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

Replace Temp with Query

The problem

Place the result of an expression in a local variable and use it in your code.

double calculateTotal(a) {
  double basePrice = quantity * itemPrice;
  if (basePrice > 1000) {
    return basePrice * 0.95;
  }
  else {
    return basePrice * 0.98; }}Copy the code

To solve

Move the entire expression into a separate function and return the result. Use query functions instead of variables. You can merge the new functions in other functions if necessary.

double calculateTotal(a) {
  if (basePrice() > 1000) {
    return basePrice() * 0.95;
  }
  else {
    return basePrice() * 0.98; }}double basePrice(a) {
  return quantity * itemPrice;
}
Copy the code

Introduce Parameter Object

The problem

Certain parameters naturally come together.



To solve

Replace these parameters with an object.



Preserve the Whole Object

The problem

You take values from an object and use them as arguments to a function call.

int low = daysTempRange.getLow();
int high = daysTempRange.getHigh();
boolean withinPlan = plan.withinRange(low, high);
Copy the code

To solve

Pass the entire object instead.

boolean withinPlan = plan.withinRange(daysTempRange);
Copy the code

Replace Method with Method Object

The problem

You have an excessively long function whose 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 to a separate class so that local variables are fields of that class. You can then split the function into multiple functions in this 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

Decompose Conditional expressions

The problem

You have complex conditional expressions.

if (date.before(SUMMER_START) || date.after(SUMMER_END)) {
  charge = quantity * winterRate + winterServiceCharge;
}
else {
  charge = quantity * summerRate;
}
Copy the code

To solve

Decompose the entire conditional expression into several functions based on conditional branching.

if (notSummer(date)) {
  charge = winterCharge(quantity);
}
else {
  charge = summerCharge(quantity);
}
Copy the code

Too long parameter column

Long Parameter List

A function has more than 3 or 4 input parameters.



Question why

Excessively long parameter columns can occur when multiple algorithms are combined into a single function. An input parameter in a function can be used to control which algorithm is ultimately chosen for execution.

An excessively long parameter column can also be a byproduct of decoupling dependencies between classes. For example, the code used to create a specific object needed in a function has been moved from the function to the code calling the function, but the created object is passed to the function as an argument. As a result, the original class no longer knows the relationships between objects, and dependencies have been reduced. But if you create these objects, each of them will need its own arguments, which means that the argument column is too long.

Too long a parameter column is hard to understand, and too many parameters can be inconsistent, hard to use, and have to be modified when more data is needed.

The solution



  • If making a request to an existing object can replace a parameter, then you should use itReplace Parameters with Methods. In this case, “existing object” may be a field in the class of the function, or it may be another parameter.
  • You can also usePreserve the Whole ObjectCollect a bunch of data from the same object and replace it with that object.
  • If some data does not have proper object ownership, it can be usedIntroduce Parameter ObjectMake a “parameter object” for them.

earnings

  • Easier to read, shorter code.
  • Refactoring may expose duplicates of code that were previously unnoticed.

When to ignore

  • There is one important exception: sometimes you obviously don’t want to create some kind of dependency between the “called object” and the “larger object”. It also makes sense to separate the data from the object and use it as a parameter. But notice the cost. If the parameter column is too long or changes too frequently, you may need to rethink your dependency structure.

Reconstruction method Description

Replace Parameters with Methods

The problem

The object calls a function and passes the result as an argument to another function. The function that accepts this parameter can itself call the previous function.

int basePrice = quantity * itemPrice;
double seasonDiscount = this.getSeasonalDiscount();
double fees = this.getFees();
double finalPrice = discountedPrice(basePrice, seasonDiscount, fees);
Copy the code

To solve

Let the argument receiver remove the argument and call the previous function directly.

int basePrice = quantity * itemPrice;
double finalPrice = discountedPrice(basePrice);
Copy the code

Preserve the Whole Object

The problem

You take values from an object and use them as arguments to a function call.

int low = daysTempRange.getLow();
int high = daysTempRange.getHigh();
boolean withinPlan = plan.withinRange(low, high);
Copy the code

To solve

Pass the entire object instead.

boolean withinPlan = plan.withinRange(daysTempRange);
Copy the code

Introduce Parameter Object

The problem

Certain parameters naturally come together.



To solve

Replace these parameters with an object.



Further reading

  • Bad code smell and refactoring
  • Code bloat with bad code smell
  • Bad taste of code abuse of object orientation
  • Barriers to code bad taste change
  • Code that doesn’t necessarily smell bad
  • Coupling of bad code smells

The resources

  • Refactoring – Improving the design of existing code – by Martin Fowler
  • sourcemaking.com/refactoring