《重构-改善既有代码的设计》第一版

简介

重构的前提:先构建安全网,然后在不改变功能外在行为的前提下重构。

重构的心法:旧的不变,新的创建,一步切换,旧的再见。

代码坏味道列表

有些重构手法没有包含在这份列表中。

Duplicated cod 重复代码

重构方式

  • Extract Method:同一个类中有重复代码,则提取公共函数;
  • Extract Class:如果某个类做了应该由两个类做的事情,那么创建一个新类,将相关的字段和函数从旧类搬移到新类;
  • Pull up Method:如果兄弟类中有重复代码,则提取公共函数后,再pull up method到父类;
  • Form Template Method:如果兄弟类之间只是代码类似,并非完全相同,则运用Extract Method将相似部分和差异部分分隔开,然后使用模板方法设计模式,将共同的部分放在父类中,差异部分在子类中分别实现;
  • 如果是不相关的两个类有重复的代码,那么可以抽取到第三个类中,如果放在两个类中的某个类,具体视情况而定。

Long Method过长函数

  • Extract Method:拆分成若干函数。每当感觉需要以注释来说明的时候,我们就把需要说明的东西写进一个独立函数中,并以其用途(而非实现手法)命名。
  • Replace Temp With Query以查询取代临时变量:有时临时变量或表达式可能在类中的多个函数中被使用,那么此时可以将变量或者表达式抽取为了类中的查询函数,供多个函数一起使用。
  • Replace Method with Method Object以函数对象取代函数:如果有一个大型函数,其中对局部变量的使用使得无法采用Extract Method。那么就将这个函数放进一个单独对象中,此时局部变量就成了对象内的字段。然后就可以在同一个对象中将这个大型函数分解为多个小型函数。
  • Decompose Conditional分解表达式:如果有一个复杂的条件(if-then-else)语句,那么就将3个段落分别提炼为独立函数。

Large Class 大类

  • Extract Class:如果某个类做了应该由两个类做的事情,那么创建一个新类,将相关的字段和函数从旧类搬移到新类;
  • Extract subClass:如果类中的某些特征只被某些实例用到,那么就新建一个子类,将部分实例使用的特征移到子类中。
  • Extract Interface:如果有多个类使用某个类接口中的同一个子集,或者两个类的接口有部分相同,那么就将相同的子集提炼到一个独立接口中。
  • Replace Data Value with Object以对象取代数据值:如果数据项要和其他数据以及行为一起使用才有意义,那么就应该将数据项封装为对象。

Long Parameter List 过长参数列表

将参数封装成结构或者类。

  • Replace Parameter with Method以函数取代参数:对象调用某个函数,并将所得结果作为参数,传递给另一个函数,而接受该参数的函数本身也能够调用前一个函数。此时让参数接受者去掉该参数,并直接调用前一个函数。
int basePrice = _quantity * _itemPrice;
discountLevel = getDisscountLevel();
double finalPrice = discountedPrice(basePrice, discountLevel);

改为

int basePrice = _quantity * _itemPrice;

double finalPrice = discountedPrice(basePrice);

private double getDisscountLevel(int basePrice){
    discountLevel = getDisscountLevel();
}
  • Introduce Parameter Object引入参数对象:某些参数总是很自然地同时出现,此时以一个对象取代这些参数。
  • Preserve Whole Object保持对象完整:从某个对象中取出若干值,将它们作为某一次函数调用的参数;此时改为传递整个对象。

Divergent Change:发散式变化

发散式修改,是指一个类受多种变化的影响。改其他需求时,都会动它,说明该类承担了过多的职责,需要采用Extract Class方法,将其拆成多个类,分离成不变和可变部分,将总是一起变化的东西放在一块儿。多个Fan in.

Shotgun Surgery:散弹式修改

改某个需求的时候,要改很多类。将各个修改点,集中起来,抽象成一个新类。多个Fan out.

  • Move Method:某个函数与其所在类之外的另一个类镜像更多交流:调用后者或者被后者调用;此时在该函数最常引用的类中建立一个有着类似行为的新函数,将旧函数变成一个单纯的委托类,或是将旧函数完全移除。
  • Move Field:某个字段被其所在类之外的另一个类更多地使用,此时在目标类新建一个字段,修改源字段的所有使用者,令它们改用该新字段。
  • Inline class将类内联化:某个类没有做太多事情,此时将该类的所有特性搬到另一个类中,然后移除原类。和Extract Class刚好相反。

Feature Envy 依恋情结

使用了大量其他类的成员.
将这个函数挪到那个类⾥里里⾯面。

  • Move Method:
  • Move Field:
  • Extract Method:

Data Clumps 数据团

经常一起出现的一组数据,给它们创建⼀个新的类。

  • Extract Class:如果某个类做了应该由两个类做的事情,那么创建一个新类,将相关的字段和函数从旧类搬移到新类;
  • Introduce Parameter Object:某些参数总是很自然地同时出现,此时以一个对象取代这些参数。
  • Preserve Whole Object:从某个对象中取出若干值,将它们作为某一次函数调用的参数;此时改为传递整个对象。

Primitive Obsession 基本类型偏执

热衷于使用int,long,String等基本类型。反复出现的一组参数,有关联的多个字段转换成类。

  • Replace Data Value with Object:如果数据项要和其他数据以及行为一起使用才有意义,那么就应该将数据项封装为对象。
  • Extract Class:如果某个类做了应该由两个类做的事情,那么创建一个新类,将相关的字段和函数从旧类搬移到新类;
  • Introduce Prameter Object:某些参数总是很自然地同时出现,此时以一个对象取代这些参数。
  • 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");
  • Replace Type code with class以类取代类型码:类之中有一个数值类型码,但它并不影响类的行为。此时以一个新类替换该数值类型码。
class Person{
    int O;
    int A;
    int B;
    int AB;
}

其实用枚举更合适点。

  • Replace Type Code with Subclasses以子类取代类型码:如果有一个不可变的类型码,它会影响类的行为。那么以子类取代这个类型码。
class Employee{
    int ENGINEER;
    int SALESMAN
}

重构为:
class Employee{
    
}

class Engineer extends Employee{
    
}

class Salesman extends Employee{
    
}
  • Repalce Type code with State/Strategy以State/Strategy取代类型码:
class Employee{
    int ENGINEER;
    int SALESMAN
}

重构为:
class Employee{
    void setType(int arg){
        _type = EmployeeType.newType(arg);
    }
}

class EmployeeType{

    static EmployeeType newType(int code){
        switch(code){
            case ENGINEER: return new Engineer();
            case SALESMAN: return new Salesman();
            default:
            throw new IllegalArgumentException("Incorrent Employee Code");
        }
    }
    
    static final ENGINEER = 0;
    static final SALESMAN = 1;
}

class Engineer extends EmployeeType{
    
}

class Salesman extends EmployeeType{
   
}

Switch Statements switch语句

  • Replace Conditional with Polymorphisrn以多态取代条件表达式:将switch表达式的每个分支放进一个子类内的覆写函数中,然后将原始函数声明为抽象函数。
  • Replace Type Code with Subclasses:
  • Replace Type Code with State/Strategy:
  • 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;
    }
}

重构为:
void setHeight(int value){
    _height = value;
}

void setWidth(int value){
    _width = value;
}
  • Introduce Null Object引入Null对象:将null值替换为null对象。

Lazy Class:冗赘类

如果它不干活了,就炒掉它。把这些不再重要的类里面的逻辑,合并到相关类,删掉旧的。

  • Inline Class:
  • Collapse Hierarchy折叠继承体系:如果超类和子类之间无太大区别,就将它们合为一体。

Parallel Inheritance Hierarchies:平行继承体系

增加A类的子类ax,B类也需要相应的增加一个bx。
应该有一个类是可以去掉继承关系的。

  • Move Method:
  • Move Field:

Speculative Generality:夸其谈未来性

过度设计,兴奋需求,直接删除。

  • Inline Class:
  • Collapse Hierarchy:
  • Remove Parameter:函数本体不再需要某个参数,此时将该参数去除。
  • Rename Method:函数的名称未能揭示函数的用途,此时修改函数名称。

Message Chains:过度耦合的消息链

过度耦合的才是坏的。

  • Hide Delegate隐藏"委托关系":客户通过一个委托类来调用另一个对象。此时在服务类上建立客户所需的所有函数,用以隐藏委托关系。
client class -> Person和Deparment
Person -> Department
重构为:
Client class -> Person -> Department

Temporary Field:令人迷惑的临时字段

仅在特定环境下使用的变量。将这些临时变量集中到一个新类中管理。

  • Extract Class:
  • Introduce Null Object:

Middle Man 中间人

大部分都交给中间人来处理了。用继承替代委托。

  • Remove Middle Man
  • Inline Method:
  • Replace Delegation with Inheritance以继承取代委托:你在两个类之间使用委托关系,并经常为整个接口,编写许多极简单的委托函数。此时让委托类继承受拖类。

Inappropriate Intimacy太亲密

两个类彼此使用对方的私有的东西。
划清界限拆散,或合并,或改成单项联系。

  • Move Method:
  • Move Field:
  • Change Bidirectional Association to Unidirectional将双向关联改为单向关联:两个类之间有双向关联,但其中一个类如今不再需要另一个的特征。此时去掉不必要的关联。
  • Replace Inheritance with Delegation以委托取代继承:某个子类只使用超类接口中的一部分,或是根本不需要继承而来的数据。此时在子类中新建一个字段用以保存超类;调整子类函数,令它改而委托超类;然后去掉两者之间的继承关系。
  • Hide Delegate:

Alternative Classes with Different Interfaces异曲同工的类

重命名,移动函数,或抽象子类。

  • Rename Method:
  • Move Method:

Incomplete Library Class:不完善的类库

包一层函数或包成新的类。

  • Introduce Foreign Method:
  • Introduce Local Extension:

Data Class:纯数据类

类很简单,仅有公共成员变量,或简单操作函数。将相关操作封装进去,减少public成员变量。

  • Move Method:
  • Encapsulate Field:
  • Encapsulate Collection:

Refused Bequest 被拒绝的遗赠

父类里面方法很多,子类只用有限几个。用代理理替代继承关系。

  • Replace Inheritance with Delegation:

Comments 太多注释

这里指代码太难懂了,不得不用注释解释。|避免用注释解释代码,而是说明代码的目的,背景等。好代码会说话。

  • Extract Method:
  • Introduce Assertion引入断言:某一段代码需要对程序状态做出某种假设。此时以断言明确表现这种假设。

你可能感兴趣的:(读书笔记)