- 1 Self Encapsulate Filed 自封装字段
- 2 Replace Data Value with Object 以对象取代数据值
- 3 Change Value to Reference 将值对象改为引用对象
- 4 Change Reference to Value 将引用对象改为值对象
- 5 Replace Array with Object 以对象取代数组
- 6 Duplicate Observed Data 复制"被监视数据"
- 7 Change Unidirectional Association to Bidirectional 将单向关联改为双向关联
- 8 Change Bidirectional Association to Unidirectional 将双向关联改为单向关联
- 9 Replace Magic Number with Symbolic Constant 以字面常量取代魔法数
- 10 Encapsulate Field 封装字段
- 11 Encapsulate Collection 封装集合
- 12 Replace Record with Data Class 以数据类取代记录
- 13 Replace Type Code with Class 以类取代类型码
- 14 Replace Type Code with Subclasses 以子类取代类型码
- 15 Replace Type Code with State/Strategy 以 State/Strategy 取代类型码
- 16 Replace Subclass with Fields 以字段取代子类
1 Self Encapsulate Filed 自封装字段
-
你直接访问一个字段, 但是与字段之间的耦合关系变得笨拙, 降低字段访问权限, 为这个字段建立取值/设值函数, 并且只以这些函数来访问这些字段.
-
间接访问变量的好处是, 子类可以通过覆写取值/设值函数来改变获取数据的途径, 而且还支持延迟初始化.
-
直接访问变量的好处是代码易于阅读.
-
注意不要再构造函数中使用设值函数
2 Replace Data Value with Object 以对象取代数据值
-
你有一个数据项,需要与其他数据和行为一起使用才有意义. 将数据项变成对象.
-
值对象应该是不可修改内容的, 这可以避免一些别名问题.
3 Change Value to Reference 将值对象改为引用对象
- 你从一个类衍生出许多彼此相等的实例,希望将它们替换为同一个对象, 将这个值对象变成引用对象.
4 Change Reference to Value 将引用对象改为值对象
- 你有一个引用对象,很小且不可变,而且不易管理, 将它变成值对象.
5 Replace Array with Object 以对象取代数组
- 你有一个数组,其中的元素各自代表不同的东西, 以对象替换数组,其中的数组中的每个元素,以一个字段来表示, 数组应该用于容纳一组有序的相似的对象.
6 Duplicate Observed Data 复制"被监视数据"
你有一些领域数据置身于GUI控件中,而领域函数需要访问这些数据.
思路: 将该数据复制到一个领域对象中.建立一个Observer模式,用以同步领域对象和GUI对象内的重要数据.
7 Change Unidirectional Association to Bidirectional 将单向关联改为双向关联
两个类都需要使用双方特性,但其间只有一条单向连接.
思路: 添加一个反向指针,并使修改函数能够同时更新两条连接.
8 Change Bidirectional Association to Unidirectional 将双向关联改为单向关联
两个类之间有双向关联,但其中一个类如今不再需要另一个类的特性.
思路: 去除不必要的关联.
9 Replace Magic Number with Symbolic Constant 以字面常量取代魔法数
-
你有一个字面数值,带有特别含义, 创造一个常量,根据其意义为它命名,并将上述的字面数值替换为这个常量.
-
可将常量提取到常量类中.
10 Encapsulate Field 封装字段
-
你的类中存在一个 public 字段, 将它声明为private, 并提供相应的访问函数.
-
除常量类外, 尽量避免在类中声明public字段.
-
如果客户端修改了该字段, 将此引用点替换为设值函数的调用, 如果客户端读取了该字段, 将此引用点替换为取值函数的调用
-
如果这个字段是各对象, 而客户端只不过是调用该对象的函数, 那么该函数是否改变对象的状态, 都只能算是读取该字段, 只有客户端为该字段重新赋值时, 才能将其替换为设值函数.
11 Encapsulate Collection 封装集合
有一个函数返回一个集合.
思路: 让这个函数返回该集合的一个只读副本,并在这个类中提供添加/移除集合元素的函数.
动机:
我们常常会在一个类中使用集合(collection,可能是array,list,set或vector)来保存一组实例.这样的类通常也会提供指针对该集合的取值/设值函数.
但是,集合的处理方式应该和其他种类的数据略有不同.取值函数不该返回集合自身,因为这会让用户得以修改集合内容而集合拥有者却一无所悉.这也会对用户暴露过多对象内部数据结构的信息.如果一个取值函数确实需要返回多个值,它应该避免用户直接操作对象内所保存的集合,并隐藏对象内与用户无关的数据结构.至于如何做到这一点,视你使用的 Java 版本不同而有所不同.
另外,不应该为这整个集合提供一个设值函数,但应该提供用以为集合添加/移除元素的函数.这样,集合拥有者(对象)就可以控制元素的添加和移除.
如果你做到以上几点,集合就可以很好地封装起来了,这便可以降低集合拥有者和用户之间的耦合度.
12 Replace Record with Data Class 以数据类取代记录
- 你需要面对传统编程环境中的记录结构, 为该记录创建一个"哑"数据对象.
13 Replace Type Code with Class 以类取代类型码
类之中有一个数值类型码,但它并不影响类的行为.
思路:以一个新的类替换该数值类型码.
14 Replace Type Code with Subclasses 以子类取代类型码
你有一个不可变的类型码,它会影响类的行为.
思路: 以子类取代这个类型码.
15 Replace Type Code with State/Strategy 以 State/Strategy 取代类型码
你有一个类型码,它会影响类的行为,但你无法通过集成手法消除它.
思路: 以状态对象取代类型码.
16 Replace Subclass with Fields 以字段取代子类
你的各个子类的唯一差别只在"返回常量数据"的函数身上.
思路: 修改这些函数,使它们返回超类中的某个(新增)字段,然后销毁子类.
动机:
建立子类的目的,是为了增加新特性或变化其行为.有一种变化行为被称为"常量函数(constant method)",它们会返回一个硬编码的值.这东西有其用途: 你可以让不同的子类中的同一个访问函数返回不同的值.你可以在超类中将反问函数声明为抽象函数,并在不同的子类中让它返回不同的值.
尽管常量函数有其用途,但若子类只有常量函数,实在没有足够的存在价值.你可以在超类中设计一个与常量函数返回值相应的字段,从而完全去除这样的子类.如此一来就可以避免因继承而带来的额外复杂性.