氣味代碼與重構(gòu)

整理自: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.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • rljs by sennchi Timeline of History Part One The Cognitiv...
    sennchi閱讀 7,817評論 0 10
  • 一直以來,都會有意的去避開看青春校園類電影,像《那些年》《致青春》《匆匆那年》《同桌的你》《微微一笑》…… 這些電...
    鄒小芝閱讀 404評論 0 0
  • 定義:定義一個操作中算法的框架,而將一些步驟延遲到子類中,使得子類可以不改變算法的結(jié)構(gòu)即可重定義該算法中的某些特定...
    萌媽碼碼閱讀 196評論 0 0
  • 世間有兩大悲哀:一種是不讀書,一種是讀書太多了。讀書就跟汽車加油一樣,適當(dāng)加一點,跑得很快,加得太多了,就變成油罐...
    007莫圣書閱讀 506評論 0 0
  • 半夜二點十分。 我關(guān)燈,爬上床,被子還沒揭開。 有人敲門。 我開門,沒人。 有人敲門。 我開門,沒人。 有人敲門。...
    迷路的浮士德閱讀 1,774評論 1 1

友情鏈接更多精彩內(nèi)容