5. 重新组织对象(数据)

程序中处理的所有对象都是数据,所有抽象、算法都是为了表示数据、处理数据。下面我们就看看如何更好的组织数据。

5.1 Self Encapsulate Field (自封装字段)
在同一个类中如果直接访问这个类的一个字段,则与字段之间的 耦合关系很深,为这个字段建立取值/设值函数,并且只以这些函数来访问字段。就如有一个原则“尽可能让所有的字段都是private的”,其实可变字段即使只通过getter/setter字段访问,也会有安全隐患,除非使用 保护性拷贝等手段。

在“ 字段访问方式”这个问题上,存在两种截然不同的观点:
(1)在该变量定义所在的类中,可以自由访问它。
(2)即使在这个类中也应该只使用访问函数间接访问。
个人支持第一种,除非对于一个变量每次使用的都是经过一定 算法计算后的值,那么可以把这个算法包装起来。当然,智者见智。

5.2 Replace Data Value with Object (以对象取代数据值)
有一个数据项,需要与其他数据和行为一起使用才有意义,就可以将数据项变成对象,封装数据和行为。

开发初期,我们往往决定以 简单的数据项表示简单的情况。但是,随着开发的进行,这些简单数据项不再那么简单了。比如 一开始可能用一个String来表示“电话号码”,但 后来发现,电话号码需要“格式化”,“提取区号”之类的特殊行为。这样Duplicate Code和Feature Envy味道很快就会从代码中散发出来。当这些坏味道开始出现,就应该讲数据值变成对象了。这是一个演变的过程,随着需求的变化,组织的变化,我们代码的处理方式也要随之变化。

5.3 Change Value to Reference (将值对象改为引用对象)
当从一个类衍生出许多彼此相等的实例,我们希望将他们替换为同一个对象。

也就是尽可能少的创建对象。如果多个对象里包含的是同一个对象(比如Date),那么就可以采用引用而不需要为每个对象创建一个对象。这可以通过使用静态工厂方法或单例模式等来实现。

5.4 Replace Array with Object (以对象取代数组)
如果有一个数组,其中的元素各自代表不同的东西。那么可以以对象来替代数组。对于数组中的每个元素变成对象的一个字段。

相信很少有人会犯这种错误。 一个数组的元素一般都是同一个范畴的数据,否则结构会很乱

5.5 Duplicate Observed Data (复制“被监视数据”)
比如有一些领域数据置身与GUI控件中,可以把这些领域数据单独组成一个对象,利用观察者模式。这样可以解耦数据与展示,对相同的数据可以有不同的展示方式。这也是基于良好的分层结构,将业务处理逻辑与界面展示层分开。


5.6 Replace type code with subclasses (以子类取代类型码)
如果有一个 不可变的类型码,而且它会影响类的行为(根据不同的值执行不同的动作)。可以以子类取代这个类型码。


一般来说, 这种情况的标志就是像switch这样的条件表达式。可能有两种表现形式:switch语句或if-else结构。不论哪种形式,都是检查类型码值,并 根据不同的值执行不同的动作,这时可以以replace conditional with polymorphism进行重构。
为了能顺利进行重构,首先应该将类型码替换为可拥有多态行为的 继承体系。这样的一个继承体系应该以类型码的宿主类为基类,并针对每一种类型码各建立一个子类。然后把不同的行为分别放到对应的子类中,选择不同的子类型执行不同的行为来代替以前的switch判断。
但有以下两种情况 不能这么做:
(1)类型码值在对象建立之后发生了改变。
(2)由于其他原因,类型码宿主类已经有了子类。这就需要使用replace type code with State/Strategy。这其实是“继承和组合”之间的抉择, 当然还是优先推荐组合。

特别注意:
并不是所有的只要出现switch就需要替换掉类型码。只是当类型码 重复出现决定类的行为,重复出现根据type code来做不同的行为时,才需要动手重构。毕竟需要判断的地方是不能省的。

5.7 replace type code with State/Strategy (以State/Strategy取代类型码)
有一个类型码,它会影响类的行为,但无法通过继承手法消除它,还可以使用状态对象取代类型码。


State模式和Strategy模式非常相似,因此无论选择其中哪个,重构过程都是相同的。

猜你喜欢

转载自zoroeye.iteye.com/blog/2199274