Self Encapsulate Field (自封装字段)
- 现象:你直接访问一个字段,但与字段之间的耦合逐渐变得笨拙
- 做法:为这个字段建立取值/设值函数,并且只以这些函数来访问字段
- 好处:
- 子类可以通过覆写一个函数而改变获取数据的途径;
- 支持更灵活的数据管理方式;
Replace Data Value with Object (以对象取代数据值)
- 现象:你有一个数据项,需要与其他数据和行为一起使用才有意义
- 做法:将数据项变成对象
- 值对象,意味着 new 一个对象
Change Value to Reference (将值对象改为引用对象)
- 现象:你从一个类衍生出许多彼此相等的实例,希望将它们替换为同一个对象
- 做法:将这个值对象变成引用对象
- 预先创建好的对象放入表中,通过工厂函数(create),查表获取
- 使用工厂函数时,一般构造函数声明为private
class Order {
public Order (String customer) {
_customer = Customer.create(customer);
}
private Customer _customer;
};
class Customer {
public static Customer create(String name) {
return (Customer) _instances.get(name);
}
private Customer(String name) {
_name = name;
}
static void loadCustomers() {
new Customer("Lemon Car Hire").store();
new Customer("Associated Coffee Machines").store();
new Customer("xxx").store();
}
private void store() {
_instances.put(this.getName(), this);
}
private static Dictionary _instances = new Hashtable();
};
Change Reference to Value (将引用对象改为值对象)
- 现象:你有一个引用对象,很小且不可变,而且不易管理
- 做法:将它变为一个值对象
Replace Array with Object (以对象取代数组)
- 现象:你有一个数组,其中的元素各自代表不同的东西
- 做法:以对象替换数组,对于数组中的每个元素,以一个字段来表示
Duplicate Observed Data (复制“被监视数据”)
- 现象:你有一些领域数据置身于GUI控件中,而领域函数需要访问这些数据
- 做法:将该数据复制到一个领域对象中。建立一个Observer模式,用以同步领域对象和GUI对象内的重复数据。
- 一个分层良好的系统,应该将处理用户界面和处理业务逻辑的代码分开,好处:
- 可灵活使用不同的用户界面来表现相同的业务逻辑。如果同时承担两种责任,用户界面会变得过分复杂
- 与GUI隔离之后,领域对象的维护和演化会更容易,可让不同开发者负责不同部分的开发
- MVC(Model-View-Controller)模式
Change Unidirectional Association to Bidirectional(将单向关联改为双向关联)
- 现象:两个类都需要使用对方特性,但其间只有一条单向连接
- 做法:添加一个反向指针,并使修改函数能够同时更新两条连接
- 缺点:造成紧耦合
Change Bidirectional Association to Unidirectional(将双向关联改为单向关联)
- 现象:两个类之间有双向关联,但其中一个类如今不再需要使用另一个类的特性
- 做法:去除不必要的关联
Replace Magic Number with Symbolic Constant(以字面常量取代魔法数)
- 现象:你有一个字面数值,带有特别含义
- 做法:创造一个常量,根据其意义为它命名,并将上述的字面数值替换为这个常量
- 魔法数:拥有特殊意义,却又不能明确表现出这种意义的数字。
Encapsulate Field(封装字段)
- 现象:你的类存在一个public字段
- 做法:将它声明为private,并提供相应的访问函数
- 面向对象的首要原则之一就是封装,或者称为“数据隐藏”。
- 尝试将使用访问函数的代码,移到访问函数所在的类中
Enapsulate Collection(封装集合)
- 现象:有个函数返回一个集合
- 做法:让这个函数返回该集合的一个只读副本,并在这个类中提供添加、移除集合元素的函数。
- 取值函数不该返回集合自身,这会让用户能够修改集合内容而集合拥有者却一无所悉,也会对用户暴露过多对象内部数据结构的信息。
- 提供添加、删除集合元素的函数。
Replace Record with Data Class(以数据类取代记录)
- 现象:你需要面对传统编程环境中的记录结构
- 做法:为该记录创建一个“哑”数据对象
- 新建一个类,表示这个记录
- 对于记录中的每一项数据,在新建类中建立对应的一个private字段,并提供相应的取值、设置函数
Replace Type Code with Class(以类取代类型码)
- 现象:类之中有一个数值类型码,但它并不影响类的行为,只是对象的一个属性
- 做法:以一个新类替换该数值类型码
- 以便编译器可以对这个类进行类型检查
Replace Type Code with Subclasses(以子类取代类型码)
- 现象:你有一个不可变的类型码,它会影响类的行为
- 做法:以子类取代这个类型码,借助多态来处理变化行为
- 标志:像switch这样的条件表达式。switch语句或者if-then-else结构。
- 为了能够顺利进行多态重构,首先应该将类型码替换为可拥有多态行为的继承体系,这样的一个继承体系应该以类型码的宿主类为基础,并针对每一种类型码各建立一个子类
- 如果宿主类中并没有出现条件表达式,那么Replace Type Code with Class更合适,风险也比较低
Replace Type Code with State/Strategy
- 现象:你有一个类型码,它会影响类的行为,但你无法通过继承手法消除它
- 做法:以状态对象取代类型码,宿主类包含类型对象作为属性
- 类型码的值在对象生命期中发生变化(工程师升任经理) 或 其他原因使得宿主类不能被继承,可用此重构手法。
Replace Subclass with Fields (以字段取代子类)
- 现象:你的各个子类的唯一差别只在“返回常量数据”的函数身上
- 做法:修改这些函数,使他们返回超类中的某个(新增)字段,然后销毁子类
- 建立子类的目的,是为了增加新特性或变化其特性
- 常量函数:返回一个硬编码的值。常量函数有其用途,若子类中只有常量函数,实在没有足够的存在价值。