重新组织数据
如果你看到一个数组的行为方式很像一个数据结构, 就可以使用 用对象取代数组
把数组变成对象, 从而使这个数据结构更清晰的显露出来. 但这才是第一步, 当你使用移动方法
为这个新对象加入相应行为时, 真正的好处才得以体现.
魔法数 --- 也就是带有特殊含义的数字, 你一定会在几天后忘了它的意思, 使用以字面常量取代魔法数
以绝后患.
8.1 Self Encapsulate Field (自封装字段)
你直接访问一个字段, 但与字段之间的耦合关系逐渐变得笨拙
为这个字段建立取值/设值函数, 并且只以这些函数来访问字段.
间接访问变量的好处是, 子类可以通过覆写一个函数而改变获取数据的途径; 它还支持更灵活的数据管理方式, 例如:延迟初始化(懒加载)
如果你想访问超类中的一个字段, 却又想在子类中将对这个变量的访问改为一个计算之后的值, 这就是最该使用本条规则的时候.
8.2 Replace Data Value with Object (以对象取代数据值)
你有一个数据项, 需要与其他数据和行为一起使用才有意义.
将数据项变为对象.
开发初期, 你往往决定以简单的数据项表示简单的情况. 但是随着开发的进行, 你发现简单数据项不在那么简单了. 它们包含了各种不同的含义.
8.3 Change Value to Reference (将值对象改为引用对象)
你从一个类衍生出许多彼此相等的实例, 希望将它们替换为同一个对象
将这个值对象变成引用对象
- 引用对象:
客户
,账户
这样的东西, 每个对象代表真实世界中的一个实物. - 值对象:
日期
,钱
这样的东西, 它们完全由其所含的数据值来定义,你并不在意副本的存在.
8.4 Change Reference to Value (将引用对象改为值对象)
你有一个引用对象, 很小且不可变, 而且不易管理
将它变成一个值对象
8.5 Replace Array with Object (以对象取代数组)
你有一个数组, 其中的元素各自代表不同的东西
以对象替换数组. 对于数组中的每个元素, 以一个字段来表示.
数组应该只用于以某种顺序容纳一组相似对象
.
8.6 Duplicate Observed Data (复制'被监视数据')
你有一些业务数据置身于GUI控件中, 而业务逻辑方法需要访问这些数据.
-
领域
此处等同于domain 或 业务
将该数据复制到一个业务对象中. 建立一个Observer模式, 用以同步业务对象和GUI对象内的重复数据.
一个分层良好的系统, 应该将处理用户界面和处理业务逻辑的代码分开.
- 1,你可能需要使用不同的用户界面来表现相同的业务逻辑, 如果同时承担两种责任, 用户界面会变得过分复杂;
- 2,与GUI隔离之后, 领域对象的维护和演化都会更容易, 你甚至可以让不同的开发者负责不同部分的开发.
8.7 Change Unidirectional Association to Bidirectional (将单向关联改为双向关联)
两个类都需要使用对方特性, 但其间只有一条单向连接.
添加一个反向指针, 并使修改函数能够同时更新两条连接.
8.8 Change Bidirectional Association to Unidirectional (将双向关联改为单向向关联)
两个类之间有双向关联, 但其中一个类如今不再需要另一个类的特性
去除不必要的关联.
双向关联很有用, 但你也必须为它付出代价, 那就是维护双向连接, 确保对象被正确创建和删除而增加的复杂度.
大量的双向关联容易造成僵尸对象
: 某个对象本来已经死亡, 却仍然保留在系统中, 因为对它的引用还没有完全清除.
8.9 Replace Magic Number with Symbolic Constant (以字面常量取代魔法数)
你有一个字面数值, 带有特别含义
创造一个常量, 根据其意义为它命名, 并将上述的字面数值替换为这个常量.
8.10 Encapsulate Field (封装字段)
你在类中存在一个public
字段
将它声明为
private
, 并提供相应的访问函数
8.11 Encapsulate Collection (封装集合)
有个函数返回一个集合
让这个函数返回该集合的一个只读副本, 并在这个类中提供添加/移除集合元素的函数.
8.12 Replace Record with Data Class (以数据类取代记录)
你需要面对传统编程环境中的记录结构
为该记录创建一个
哑
数据对象.
比如一个遗留接口, 或者从数据库取值. 这些时候你就有必要创建一个接口类, 用以处理这些外来数据.
8.13 Replace Type Code with Class (以类取代类型码)
类之中有一个数值类型码, 但它并不影响类的行为.
以一个新的类替换该数值类型码.
在使用本条之前, 你应该先考虑类型码的其他替换方式.
只有当类型码是纯数据时(也就是类型码不会再switch语句中引起行为变化时), 你才能以类来取代它,
更重要的是: 任何switch语句都应该运用以多态取代条件表达式
去掉.
为了进行那样的重构, 你首先必须运用用子类替换类型码
或以状态/策略模式取代类型码
8.14 Replace Type Code with Subclasses (以子类取代类型码)
你有一个不可变的类型码, 他会影响类的行为
以子类取代这个类型码
如果你面对的类型码不会影响宿主类的行为, 可以使用以类取代类型码
来处理它们.
但如果类型码会影响宿主类的行为, 那么最好的办法就是借用多态
来处理变化行为.
一般来说, 这种情况的标志就是像switch
或if-else
结构.
但是以下两种情况你不能那么做:
- 1,类型码值在对象创建之后发生了改变
- 2,由于某些原因, 类型码宿主类已经有了子类.
如果你恰好面临这两种情况之一, 就需要使用以状态/策略模式取代类型码
以子类取代类型码
的好处在于: 它把对不同行为的了解
从类用户那儿转移到了类本身.
8.15 Replace Type Code with State/Strategy (以State/Strategy取代类型码)
你有一个类型码, 它会影响类的行为, 但你无法通过继承手法清除它.
以状态对象取代类型码
如果类型码的值在对象生命期中发生变化
或 其他原因使得宿主类不能被继承
你就可以使用本重构.
如果你打算完成本项重构之后再以多态取代条件表达式
简化一个算法, 选择Strategy模式比较合适
如果你打算搬移状态相关的数据, 而且你把新建对象视为一种变迁状态, 选择State模式比较合适
8.16 Replace Subclass with Fields (以字段取代子类)
你的各个子类的唯一差别只在返回常数数据
的函数身上.
修改这些函数, 使它们返回超类中的某个(新增)字段, 然后销毁子类
不同的子类在函数中硬编码了内容, 把这些返回常数数据
的函数, 所返回的数据,作为不同情况下的初始化参数, 再父类的构造函数初始化方法中对这些字段进行赋值, 如果获取对应的信息, 则调用其getter方法, 即消除了子类.
网友评论