重新组织数据
1. Self Encapsulate Field(自封装字段)
Q:你直接访问一个字段,但与字段之间的耦合关系逐渐变得笨拙
A:为这个字段建立取值/设值函数,并且只以这些函数来访问字段
间接访问变量的好处是,子类可以通过覆写一个函数而改变获取数据的途径;支持延迟初始化等。
2. Replace Data Value with Object(以对象取代数据值)
Q:你有一个数据项,需要与其他数据和行为一起使用才有意义。
A:将数据项变为对象
业务一开始可能仅仅需要一个数据值,后续慢慢加入针对数据值的各种行为和关联数据,此时 Duplicate Code 和 Feature Envy 的坏味道就会逐渐暴露。
3. Change Value to Reference(将值对象改为引用对象)
Q:你从一个类衍生出许多彼此相等的实例,希望将它们替换为同一个对象。
A:将这个值对象变成引用对象。
之所以需要将值对象改为引用对象,通常的考虑是一些重复的对象需要同步改动,更直接的例子就是单例对象。
引用对象应该由静态字典或者注册表对象来管理,应该考虑预先创建好还是动态创建,通常需要考虑使用工厂模式进行创建。
4. Change Reference to Value(将引用对象改为值对象)
Q:你有一个引用对象,很小且不可变,而且不易管理。
A:将它变成一个值对象。
引用对象必须被以某种方式控制,你必须向其控制者请求适当的对象。它们可能造成内存区域之间错综复杂的关联。而值对象,特别是不可变的值对象,使你无需考虑同步问题。
可变的引用对象是不应该变为值对象的,因为那将意味着你需要考虑各个值对象之间数据同步的问题。
5. Replace Array with Object(以对象取代数组)
Q:你有一个数组,其中的元素各自代表不同的东西。
A:以对象替换数组。对于数组中的每一个元素,以一个字段来表示。
假如一个数组的元素根据其下标具备不同的含义和行为以及数据解释方式,则对象更适合表示这些数据。
6. Duplicate Observed Data(复制“被监视数据”)
Q:你有一些领域数据置身于 GUI 控件中,而领域函数需要访问这些数据。
A:将该数据复制到一个领域对象中,建立一个 Observer 模式,用以同步领域对象和 GUI 对象内的重复数据。
简单来说,“让数据归数据,让显示归显示”;而实现这一特性离不开观察者模式。
7. Change Unidirectional Association to Bidirectional(将单向关联改为双向关联)
Q:两个类都需要使用对方的特性,但其间只有一条单向连接
A:添加一个反向指针,并使修改函数能够同时更新两条连接。
如何确定由谁控制双向关联关系:
- 如果两者都是引用对象,且关联关系是“一对多”的关系,那么就由“拥有单一引用”的那一方承担“控制者”角色
- 如果某个对象是组成另一个对象的部件,那么由后者负责控制关联关系
- 如果两者是“多对多”的关系,由谁控制关联关系都无所谓
8. Change Bidirectional Association to Unidirectional
Q:两个类之间有双向关联,但其中一个类如今不再需要另一个类的特性。
A:去除不必要的关联。
维护双向连接增加了复杂度,且容易造成内存泄漏,增加模块间耦合。
9. Replace Magic Number with Symbolic Constant(以字面常量取代魔法数)
Q:你有一个字面数值,带有特别含义。
A:创造一个常量,根据其意义为它命名,并将上述的字面数值替换为这个常量。
10. Encapsulate Field(封装字段)
Q:你的类中存在一个 public 字段
A:将它声明为 private,并提供相应的访问函数。
11. Encapsulate Collection(封装集合)
Q:有个函数返回一个集合。
A:让这个函数返回该集合的一个只读副本,并在这个类中提供添加/移除集合元素的函数。
取值函数不应该返回集合自身,因为这样用户修改集合内容时集合拥有者将一无所知。
12. Replace Record with Data Class(以数据类取代记录)
Q:你需要面对传统编程环境中的记录结构。
A:为该记录创建一个“哑”数据对象。
不要以代码之外的规则约束代码逻辑,例如以数组的不同下标来表示不同含义。
13. Replace Type Code with Class(以类取代类型码)
Q:类之中有一个数值类型码,但它并不影响类的行为。
A:以一个新的类替换该数值类型码。
类型码本质上仍然是数值,而非对象,因而编译器无法做有效的类型检查。
14. Replace Type Code with Subclasses(以子类取代类型码)
Q:你有一个不可变的类型码。
A:以子类取代这个类型码。
在面向对象术语中,听上去最高贵的词非“多态”莫属。
将类型码用子类封装后,与类型码相关的行为和特性也就可以封装到子类中。
15. Replace Type Code with State/Strategy(以 State/Strategy 取代类型码)
Q:你有一个类型码,它会影响类的行为,但你无法通过继承手法消除它。
A:以状态对象取代类型码。
16. Replace Subclass with Field(以字段取代子类)
Q:你的各个子类的唯一差别只在“返回常量数据”的函数身上。
A:修改这些函数,使它们返回超类中的某个(新增)字段,然后销毁子类。
没有行为只有数据的类应当被移除。