第7章 在对象之间搬移特性
简介:
在对象设计的过程中,“决定把责任放哪儿”是最重要的事情之一,如果没有放对地方,则采取重构。
一个类常常可以通过“搬移字段”和“搬移函数”来简单移动对象达到重构目的;
如果一个类还是臃肿不堪,则可以使用“提炼函数”的方法将一部分职责分出去;
如果一个类变得太不负责任则可以使用“将类内联化”方法来重构;
如果一个类使用了另一个类,且运用“隐藏委托关系”方法是有帮助的,则用此方法重构;
如果当委托(中间人)变得没有必要,则可以使用“删除中间人”方法;
最后“引入外加函数”和“引入本地扩展”是只有当不能访问某个类的源码,却又想把责任移进这个不修改的类时使用的两个重构方法,引进一个两个函数用前者,大于两个用后者。
Move Method(搬移函数)
1.概念:
一个函数与另外一个A类中方法的交流多于与自己B类的交流,那么在A类中建立一个有着类似行为的新函数,将旧函数变成一个单纯的委托函数,或者将旧函数完全移除。
2.动机:
“搬移函数”是重构理论的支柱。如果类有太多行为或者两个类之间合作形成了高度耦合,则考虑用搬移函数。
3.做法:
觉得适合就搬,搬的时候考虑下迁移范围。
Move Field(搬移字段)
1.概念:
在程序中的某个字段被自身类的其他类更多地用到,那么在目标类新建字段,并将之前旧字段换为新的字段。
2.动机:
一个类中的字段被另一个类更频繁地使用到了,这里的“使用”,可能是通过设置或者取值函数间接进行的。
3.做法:
顾名思义,想好就用。
Extract Class(提炼类)
1.概念:
某个类做了两个类应该做的事,则建立一个新类,将相关字段和函数从旧类搬移到新类。
2.动机:
一个类应该是一个清楚的抽象,处理一些明确的责任。当一个类不断扩展,从而变得过分复杂,就要考虑梳理,提炼和迁移。
3.做法:
(1)分解一个类中所担负的责任。
(2)建立新类,迁移,测试,从较低层函数开始。
(3)删除旧类相应逻辑,精简每个类的接口。
(4)决定是否公开新类。
4.例子:
之前我在实践中,一个类扩展得越来越复杂,于是会把事务和有业务逻辑相关的代码写在一起,后来同事给我提了很好的建议,就是将事务提出来单独放在一起,现在想到就是符合了“提炼类”的做法。
Inline Class(将类内联化)
1.概念:
某个类没有做太多事情,就可以将这个类的所有特性搬移到另一个类中,然后移除原类。
2.动机:
通常因为此前重构动作移走了这个类的责任,后因需求改动发现这个类不再承担足够责任,因此可以再用“将类内联化”重构手法,将类合并。
3.做法:
合了测,测了删。
Hide Delegate(隐藏委托关系)
1.简介:客户通过一个委托类来调用另一个对象,则在服务类上建立客户所需的所有函数,用以隐藏委托关系。
2.回顾:在第3章读书笔记《第三章 代码的坏味道》中的第15个坏味道里,过度耦合的消息链中提到了隐藏委关系,这里就不(lan)再(de)赘(xie)述(le)。
Remove Middle Man(移除中间人)
1.中间人概念:《第三章 代码的坏味道》的第16个坏味道里,介绍了中间人。
2.做法:
(1)建立一个函数,用以获得受托对象。
(2)对于每个委托函数,在服务类中删除该函数,并让需要调用的客户转为调用受托对象。
(3)处理每个委托函数后,编译,测试。
Introduce Foreign Method(引入外加函数)
1.概念:
你需要为提供服务的类增加一个函数,但你无法修改这个类。于是你可以在客户端类中建立一个函数,并以第一参数形式传入一个服务类实例。
2.动机:
你正在使用一个类,它真的很好,为你的需求提供了所有服务,而后,新的需求要求你提供一项新的服务,这个类无法供应,如果这时还不能对这个类进行修改,则就只得在客户端编码,补足你要的函数。但如果发现需要在服务类建立大量的外加函数,则就不能再用“引入外加函数”方法重构,而是使用“引入本地扩展重构”。
Introduce Local Extension(引入本地扩展)
1.概念:
你需要为服务类提供一些额外函数,但你无法修改这个类,则建立一个新类,使它包含这些额外函数,让这个扩展品成为源类的子类或包装类。
2.动机:
当你在服务端需要添加的外部函数超过了两个,则外加函数就很难控制它们了,所以你需要将这些函数组织在一起,放到一个恰当的地方去。要达到这一目的,两种标准对象技术--子类化和包装,统称为本地扩展。
使用本地扩展使你得以坚持“函数和数据应该被统一封装”的原则。
3.做法:
(1)建立一个扩展类,将它作为原始类的子类或包装类。
//用子类的方式(包装类使用暂略)
class MfDateSub extends Date
(2)在扩展类中加入转型构造函数(接受原对象作为参数的构造函数)。
//加入转型构造函数 public MfDateSub(Date arg) { super(arg.getTime()); } //处理Date和扩展类之间的不同 public MfDate(String dateString) { super(dateString); }
(3)在扩展类中加入新特性。
//搬移前 client class... private static Date nextDay(Date arg) { //foreign method, should be on date return new Date (arg.getYear(), arg.getMonth(), arg.getDate() + 1); } //搬移后 class MfDateSub... Date nextDay() { return new Date (getYear(), getMonth(), getDate() + 1); }
(4)根据需要,将原对象替换为扩展对象。
(5)将针对原始类定义的所有外加函数搬移到扩展类中。