重构(笔记)——第八章

300 阅读4分钟

重新组织数据

自封装字段

你直接访问一个字段,但与字段之间的耦合关系逐渐变得笨拙。

简而言之就是为类的某个成员变量(字段)添加 get 方法,而避免直接使用其字段名来取值。

// 书中的示例
private int low, hight;
boolean includes (int arg) {
    return arg >= low && arg <= high;
}

// 重构后
private int low, hight;
boolean includes(int arg) {
    return arg >= getLow() && arg <= getHigh();
}

int getLow(){
    return low;
}

int getHight(){
    return hight;
}

今天编程中,使用 get 和 set 方法的操作已经成为了一种习惯,并且 IDE 工具也可以自动生成。get 方法比直接读取成员变量(字段)的可控性更高一些,可以进行某过滤操作等。

以对象取代数据值

在开发的初其阶段,我们很多字段都是固定的,这时我们可能不会为一个字段去封装一个对象,而是直接使用一个变量来表示,但是后面如果字段增加的话,就要再继续增加变量。

书中提倡的是将变量也封装成对象。

// 书中的示例
class Order {
    private String customer;
    
    public Order(String customer) {
        this.customer = customer;
    }
    
    public void setCustomer(String customer) {
        this.customer = customer;
    }
    
    public String getCustomer(String customer) {
        return customer;
    }
}

// 重构后

class Order {
    private Customer customer;
    
    public Order(String name) {
        this.customer = new Customer(name);
    }
    
    public String getCustomer() {
        return customer.getName();
    }
    
    public void setCustomer(String name) {
        this.customer = new Customer(name);
    }
}

class Customer {
    private String name;
    
    public Customer(String name) {
        this.name = name;
    }
    
    public String getName() {
        return this.name;
    }
}

上面的例子,就是将原本只是一个成员变量表示的 customer 封装成了一个对象 Customer,然后再提供方法从 Customer 对象中获取值。

将值对象改为引用对象

// 值对象和引用对象,书中的理解与我们现在的理解不太相同。这也属于正常,毕竟这是一本 9x 年的书。

将引用对象改为值对象

// 与上面的操作相反

以对象取代数组

// 书中示例
String[] row = new String[];
row[0] = "Liverpool";
row[1] = "15";

// 重构后
Performance row = new Performance();
row.setName("Liverpool");
row.setAge("15");

这个很简单,用一个数组不同的下标来表示多个数据的情况下,使用一个对象来表示更合适。其实现在写代码已经没有人会用数组来存多个数据了。

复制“被监视的数据”

本质上是观察者设计模式,书中的代码示例使用了 JDK 提供的 Observer 接口和 Observable 抽象类。

将单向关联改为双向关联

两个类都需要使用对方特性,但其间只有一条单向连接。添加一个反向指针,并使修改函数能够同时更新两条连接。

将双向关联改为单向关联

以字面常量取代魔法数

创建一个常量,根据其意义为字命名,将将上述的字面数值替换为这个常量。

封装字段

本质就是将一个公开的 public 的成员变量,修改为调用其 get 和 set 方法。

public String name;

// 重构后
private String name;

public String getName(){
    return name;
}

public void setName(String name){
    this.name = name;
}

封装集合

让这个函数返回该集合的一个只读副本,并在这个类中提供添加/移除集合元素的函数。

如果在一个封装类中,包含有 集合 类型,对其提供了 get 和 set 方法,那么将 get 方法返回一个只读的副本,要向集合中添加元素时,再单独提供 add 方法进行添加 ,而不能是通过 get 方法获取到集合对象,然后调用集合对象的 API

class xxx {
    ....
    // 示例代码
    public Set<Order> getOrder(){
        retrun this.orders;
    }
    
    // 重构后
    public Set<Order> getOrder(){
        return  Collections.unmodifiableSet(courses);
    }
}

让代码各有所属,重构还有很多的手法,需要结合起来进行综合性的使用。

以数据类取代记录

这已经是我们现在的常用手段了,在编写代码的过程中,我们经常使用到的是 XxxEntity 或者称为的 POJO 对象。

以类取代类型码

现在我们的常用手段是使用枚举类来替换类型码。

以子类取代类型码

其本质是对一种特殊进行处理,如果是特殊情况下,单独抽取出来形成一个子类。

以 State/Strategy 取代类型码

这是两种设计模式,状态/策略。状态模块和策略模式其实也挺像的,在正常的过程中我们可以

以字段取代子类

// 书中的示例
abstract class Person {
    abstract boolean isMale();
    abstract char getCode();
}

class Male extends Person {
    boolean isMale(){
		return true;
    }
    
    char getCode(){
        return 'M';
    }
}

class Female extends Person{
    boolean isMale(){
        return false;
    }
    
    char getCode(){
        return 'F';
    }
}

// 重构后

class Person {
    private boolean isMale;
    private char code;
    
    static Person createMale() {
        return new Person(true, 'M');
    }
    
    static Person createFemale() {
        return new Person(false, 'F');
    }
    
    protected Person (boolean isMale, char code) {
        this.isMale = isMale;
        this.code = code;
    }
    
    boolean isMale() {
        return thid.code;
    }
    
    char getCode() {
        return this.code;
    }
    
}

参考资料:
[1]: book.douban.com/subject/426… "重构-改善既有代码的设计"