美文网首页
气味代码与重构

气味代码与重构

作者: 鹿特丹的风 | 来源:发表于2019-01-22 05:53 被阅读0次

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

    相关文章

      网友评论

          本文标题:气味代码与重构

          本文链接:https://www.haomeiwen.com/subject/diwqjqtx.html