《重构改善既有代码的设计》之重构列表--重新组织数据(五)

十四、Replace Type Code with Subclass(以子类取代类型码)

你有一个不可变的类型码,它会影响类的行为。

以子类取代这个类型码。

动机

如果你面对的类型码不会影响宿主类的行为,可以使用Replace Type Code with Class来处理它们。但如果类型码会影响宿主类的行为,那么最好的办法就是借助多态来处理变化行为。

一般来说,这种情况的标志就是像switch这样的条件表达式。这种条件表达式可能有两种形式:switch语句或者if-else-then结构。不论哪种形式,它们都是检查类型码值,并根据不同的值执行不同的动作。这种情况下,你应该以Replace Conditional with Polymorphism进行重构。单位了能够顺序进行那样的重构,首先应该将类型码替换为可拥有多态行为的继承体系。这样的一个继承体系以类型码的宿主类为基类,并针对每一种类型码各建立一个子类。

为建立这样的继承体系,最简单的办法就是Replace Type Code with Subclass:以类型码的宿主类为基类,并针对每一种类型码建立相应的子类。

但是一下两种情况你不能那么做:(1)类型码值在对象创建之后发生改变(2)由于某些原因,类型码宿主类已经有了子类。如果你恰好面临这两种情况之一,就需要使用Replace Type Code with State/Strategy

Replace Type Code with Subclass的主要作用就是搭建一个舞台,让Replace Conditional with Polymorphism 得以一展身手。如果宿主类中并没有出现条件表达式,那么Replace Type Code with Class更合适,风险也比较低。

使用Replace Type Code with Subclass的另一个原因就是:宿主类中出现了“只与具备特定类型码之对象关联”的特性。完成本项重构后,你可以使用Push Down MethodPush Down Field将这些特性推到合适的子类去,以彰显它们只与特定情况相关这一事实。

Replace Type Code with Subclass的好处在于:它把“对不同行为的了解”从类用户那儿转移到了类自身。如果需要再加入新的行为变化,只需添加一个子类就行了。如果没有多态机制,就必须找到所有条件表达式,并逐一修改它们。因此,如果未来还有可能加入新行为,这项重构将特别有价值。

做法

1、使用Self Encapsulate Field将类型码自我封装起来。

如果类型码被传递给构造函数,就需要将构造函数换成工厂函数。

2、为类型码的每一个数值建立一个相应的子类。在每个子类中覆写类型码的取值函数,使其返回相应的类型码值。

这个值被硬编码于return语句中。这看起来很肮脏,但只是权宜之计。当所有case子句都被替换后,文体就解决了。

3、每建立一个新的子类,编译并测试。

4、从超类中删掉保存类型码字段。将类型码访问函数声明为抽象函数。

5、编译、测试。

十五、Replace Type Code with State/Strategy (State/Strategy取代类型码)

你有一个类型码,它会影响类的行为,但你无法通过继承手法消除它。

以状态对象取代类型码。

动机

本项重构和Replace Type Code with Subclass很相似,但如果“类型码的值在对象生命期中发生变化”或“其他原因使得宿主类不能被继承”,你也可以使用本重构。本重构使用State模式或Strategy模式。

State模式或Strategy模式非常相似,因此无论你选择其中哪一个,重构过程都是相同的,“选择哪一模式”并非问题的关键所在,你只需要选择更适合特定情况的模式就可以了。如果你打算再完成本项重构之后再以Replace Conditional with Polymorphism简化一个算法,那么选择Strategy模式比较合适,如果你打算搬移与状态相关的数据,而且你把新对象视为一种变迁状态,就应该选择使用State模式。

做法

1、使用Self Encapsulate Field 将类型码封装起来。

2、新建一个类,根据类型码的用途为它命名。这就是一个状态对象。

3、为这个新类添加子类,每个子类对于一种类型码。

比起逐一添加,一次性加入所有必要的子类可能跟简单些。

4、在超类中建立一个抽象的查询函数,用以返回类型码。在每个子类中覆写该函数,返回确切的类型码。

5、编译。

6、在源类中建立一个字段,用以保存新建的状态对象。

7、调整源类中为类型码设值的函数,将一个恰当的状态对象子类赋值给“保存状态对象”的那个字段。

8、编译、测试。

十六、Replace Subclass with Fields(以字段取代子类)

你的各个子类的唯一差别只在“返回常量数据”的函数身上。

修改这些函数,使它们返回超类中的某个“新增”字段,然后销毁子类。

动机

建立子类的目的,是为了增加新特性或变化其行为。有一种变化行为被称为“常量函数”,它们会返回一个硬编码的值。这东西有其用途:你可以让不同的子类中的同一个访问函数返回不同的值。你可以在超类中将访问函数声明为抽象函数,并在不同的子类中让它返回不同的值。

尽管常量函数有其用途,但若子类中只有常量函数,实在没有足够的存在价值。你可以在超类中设计一个与常量函数返回值相应的字段,从而完全去除这样的子类。如此一来就可以避免因继承而带来的额外复杂性。

做法

1、对所有子类使用Replace Constructor with Factory Method

2、如果有任何代码直接引用子类,令它改而引用超类。

3、针对每个常量函数,在超类中声明一个final字段。

4、为超类声明一个protected构造函数,用以初始化这些新增字段。

5、新建或修改子类构造函数,是它调用超类的新增构造函数、

6、编译、测试

7、在超类中实现所有常量函数,令它们返回相应字段值,然后将该函数从子类中删掉。

8、每删除一个常量函数,编译并测试。

9、子类中所有的常量函数都被删除后,使用Inline Method将子类构造函数内联到超类的工厂函数中。

10、编译、测试

11、将子类删掉。

12、编译、测试。

13、重复“内联构造函数、删除子类”过程,知道所有子类都被删除。 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值