气味代码与重构

整理自:https://sourcemaking.com/refactoring/smells

类型一:Bloaters

代码、方法和类增长到庞大的量。

Long Method

一般来说,长于10行的方法就应该考虑是否需要重构。如果方法中有些地方需要注释,就应该考虑是否把这些代码放入新方法内,即使该代码只有一行,原因是如果方法名是描述性的,就不需要阅读进行实际操作的源码。

解决方案:

  • 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代表简单事物;用常数来表示信息等。

解决方案:

  • 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

一个方法包含了三个或四个以上的参数。

解决方案:

  • 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

不同部分的代码都包含了相同的一堆变量(即数据块)。如果要确定某些数据是否为数据块,只需删除其中一个数据值,然后查看其他值是否仍然有意义。如果不是,表明这组变量应该组合成一个对象。

  • 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

存在复杂的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

如果子类仅使用从其父项继承的某些方法和属性,则说明使用这种层次结构是不合适的。

解决方案:

  • 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

如果你需要在代码中的某个位置更改某些内容,则必须在其他位置进行许多更改。

Divergent Change

当对某个类进行更改时,你发现自己必须更改许多不相关的方法。

解决方案:

  • 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

进行任何修改都需要对许多不同的类进行许多小的更改。

解决方案:

  • 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

每当为类创建子类时,你发现自己需要为另一个类创建子类。

解决方案:

  • 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

如果一个类的几乎不会被用上,就应该被删除。

解决方案:

  • 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

数据类是指仅包含用于访问它们的字段和粗略方法的类(getter和setter)。这些只是其他类使用的数据的容器。这些类不包含任何其他功能,也不能独立操作它们拥有的数据。

解决方案:

  • 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

不再被使用的变量,参数,字段,方法或类。

解决方案:

  • 使用好的IDE,通过IDE标注找到没被使用的变量,参数,字段,方法或类。

  • 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

方法访问另一个对象的数据多于访问自己的数据。

解决方案:

  • 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

一个类使用另一个类的内部字段和方法。

解决方案:

  • 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

在代码中你看到类似于这样的一系列的方法调用: $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

如果一个类只执行将工作委托给另一个类,那么它就没有存在的必要。

解决方案:

  • Remove Middle Man

A class has too many methods that simply delegate to other objects.

你可能感兴趣的:(气味代码与重构)