重构-改善既有代码的设计读书笔记(七)

在对象之间搬移特性

7.1 Move Method(搬移函数)

你的程序中,有个函数与其所驻类之外的另一个类进行更多交流:调用后者,或被后者调用。在该函数最常引用的类中建立一个有着类似行为的新函数。将旧函数变成一个单纯的委托函数,或将旧函数移除。
做法:
1. 检查源类中被源函数所使用的一切特性,考虑它们是否该被搬移。
2. 检查源类的子类和超类,看看是否有该函数的其他声明。
如果出现其他声明,你或许无法搬移,除非目标类也同样表现出多态性。
3. 在目标类中声明这个函数。(注意良好的命名)
4. 将源函数的代码复制到目标函数中。调整后者,使其能在新类中正常运行。
当需要使用源类的特性时:
4.1 将这个特性也移到目标类。
4.2 建立或使用一个从目标类到源类的引用关系。
4.3 将源对象当作参数传给目标函数。
4.4 如果所需特性是个变量,将它当作参数传给目标函数。
5. 决定如何从源函数正确引用目标对象。
6. 修改源函数,使之成为一个纯委托函数。
7. 决定是否删除源函数,或将它当作一个委托函数保留下来。
如果你经常要在源对象中引用目标函数,那么将源函数作为委托函数保留下来会比较简单。

7.2 Move Field(搬移字段)

你的程序中,某个字段被其所驻类之外的另一个类更多的用到。在目标类新建一个字段,修改源字段的所有用户,令它们改用新字段。
做法:
1. 在目标类建立与源字段相同的字段,并同时建立相应的取值/设值函数。
2. 决定如何在源对象中引用目标对象。(可以通过函数或目标对象字段)
3. 删除源字段。
4. 将所有对源字段的引用替换为对某个目标函数的引用。

7.3 Extract Class(提炼类)

某个类做了应该由两个类做的事。建立一个新类,将相关的字段和函数从旧类搬移到新类。
一个类应该是一个清楚的抽象,处理一些明确的责任。
动机:
1. 一个类含有大量的函数和数据,增加了类的理解难度。
2. 子类化只影响了类的部特性,或如果某些特性需要以一种方式来子类化,某些特性则需要以另一种方式子类化,这就意味着你需要分解原来的类。
做法:
1. 决定如何分解类所负的责任。
2. 建立一个新类,用以表现从旧类中分离出来的责任。
3. 建立“从旧类访问新类”的连接关系。
4. 对于你想搬移的每一个字段,以Move Field(7.2)搬移之。
5. 使用Move Method(7.1)将必要函数搬移到新类。先搬移较低层函数(也就是“被其他函数调用”多于“调用其他函数”),再搬移较高层函数。
6. 检查,精简每个类的接口。
7. 决定是否公开新类,如果你的确需要公开它,就要决定让它成为引用对象还是不可变的值对象。对于这种情况,
7.1 运行任何对象修改新对象的任何部分。这就使得新对象成为引用对象,可以考虑使用Change Value to Reference(8.3),这种情况下,旧类应该是新类的访问点。
7.2 不许任何人不通过旧类就修改新类。为此,可以将新类设置为不可修改的,或为它提供一个不可修改的接口。
7.3 将复制得到的新类对象传递给用户。

7.4 Inline Class(将类内联化)

某个类没有做太多事情。将这个类的所有特性搬移到另一个类中,移除原类。
做法:
1. 在目标类身上声明源类的public协议,并将其中所有函数委托至源类。
如果“以一个独立接口表示源类函数”更合适的话,就应该在内联之前使用Extract Interface(11.8)。
2. 修改所有源类引用点,改而引用目标类。
3. 运用Move Method(7.1)和Move Field(7.2),将源类的特性全部转移到目标类。

7.5 Hide Delegate(隐藏“委托关系”)

客户通过一个委托来调用另一个对象。在服务类上建立客户所需的所有函数,用以隐藏委托关系。

manager = john.getDepartment().getManager();

to

public Person getManager() {
    return department.getManager();
}

manager = john.getManager();

动机:
如果某个客户先通过服务对象的字段得到另一个对象,然后调用后者的函数,那么客户就必须知晓这一层委托关系。如果委托关系发生变化,客户也得相应变化。使用隐藏委托关系,可以将这种变化限制在服务对象中,不会波及客户。
做法:
1. 对于每一个委托关系中的函数,在服务对象端建立一个简单的委托函数。
2. 调整客户,令它只调用服务对象提供的函数。
3. 每次调整后,编译并测试。
4. 如果将来不再有任何客户需要取用受托类,便可移除服务对象中的相关访问函数。

7.6 Remove Middle Man(移除中间人)

某个类做了过多的简单委托动作,让客户直接调用受托类。

public Person getManager() {
    return department.getManager();
}

manager = john.getManager();

to

manager = john.getDepartment().getManager();

动机:
使用Hide Delegate所带来的代价是:每当客户要使用受托对象的新特性时,你就必须在服务端添加一个简单委托函数。具体的隐藏尺度只能自己把握。
做法:
1. 建立一个新函数,用以获得受托对象。
2. 对于每个受托函数,在服务类中删除该函数,并让需要调用该函数的客户转而调用受托对象。

7.7 Introduce Foreign Method(引入外加函数)

你需要为提供服务的类增加一个函数,但你无法修改这个类。在客户类中建立一个函数,并以第一参数形式传入一个服务类实例。

Date newStart = new Date(previousEnd.getYear(), previousEnd.getMonth(),previousEnd.getDate()+1);

to

Date newStart = nextDay(previousEnd);

private static Date nextDay(Date previousEnd) {
    return new Date(previousEnd.getYear(), previousEnd.getMonth(),previousEnd.getDate()+1);
}

如果你发现自己为一个服务类创建了大量的外加函数,或者发现有许多类都需要同样的外加函数,就应该使用Introduce Local Extension(7.8)。
做法:
1. 在客户类中建立一个函数,用来提高你所需要的功能。
这个函数不应该调用客户类的任何特性。如果它需要一个值,把该值当作参数传给它。
2. 以服务类实例作为该函数的第一个参数。
3. 将该函数注释为:“外加函数,应在服务类中实现。”

7.8 Introduce Local Extension(引入本地扩展)

你需要 为服务类提供一些额外函数,但你无法修改这个类。建立一个新类,使它包含这些额外函数,让这个扩展品成为源类的子类或包装类。
做法:
1. 建立一个扩展类,将它作为原始类的子类或包装类。
2. 在扩展类中加入转型构造函数。
如果采用子类化方案,那么转型构造函数应该调用适当的超类构造函数;如果采用包装类方案,那么转型构造函数应该将它得到的传入参数以实例变量的形式保存起来,用以接收委托的原对象。
3. 在扩展类中加入新特性。
4. 根据需要,将原对象替换为扩展对象。
5. 将针对原始类定义的所有外加函数搬移到扩展类中。

子类化方案中,一般不要在扩展类中覆写原始类的函数,最好添加新函数;包装类方案中,可以通过重载函数,分别接收原始类对象为参数和包装类对象为参数,这样就不必检查未知对象的类型。

重构,一言以蔽之,就是在不改变外部行为的前提下,有条不紊地改善代码。多年前,正是本书原版的出版,使重构终于从编程高手们的小圈子走出,成为众多普通程序员日常开发工作不可或缺的一部分。本书也因此成为与《设计模式》齐名的经典著作,被译为、德、俄、日等众多语言,在世界范围内畅销不衰。 本书凝聚了软件开发社区专家多年摸索而获得的宝贵经验,拥有不因时光流逝而磨灭的价值。今天,无论是重构本身,业界对重构的理解,还是开发工具对重构的支持力度,都与本书最初出版时不可同日而语,但书所蕴涵的意味和精华,依然值得反复咀嚼,而且往往能够常读常新。 第1章 重构,第一个案例 1.1 起点 1.2 重构的第一步 1.3 分解并重组statement() 1.4 运用多态取代与价格相关的条件逻辑 1.5 结语 第2章 重构原则 2.1 何谓重构 2.2 为何重构 2.3 何时重构 2.4 怎么对经理说 2.5 重构的难题 2.6 重构设计 2.7 重构与性能 2.8 重构起源何处 第3章 代码的坏味道 3.1 Duplicated Code(重复代码) 3.2 Long Method(过长函数) 3.3 Large Class(过大的) 3.4 Long Parameter List(过长参数列) 3.5 Divergent Change(发散式变化) 3.6 Shotgun Surgery(霰弹式修改) 3.7 Feature Envy(依恋情结) 3.8 Data Clumps(数据泥团) 3.9 Primitive Obsession(基本型偏执) 3.10 Switch Statements(switch惊悚现身) 3.11 Parallel InheritanceHierarchies(平行继承体系) 3.12 Lazy Class(冗赘) 3.13 Speculative Generality(夸夸其谈未来性) 3.14 Temporary Field(令人迷惑的暂时字段) 3.15 Message Chains(过度耦合的消息链) 3.16 Middle Man(间人) 3.17 Inappropriate Intimacy(狎昵关系) 3.18 Alternative Classes with Different Interfaces(异曲同工的) 3.19 Incomplete Library Class(不完美的库) 3.20 Data Class(纯稚的数据) 3.21 Refused Bequest(被拒绝的遗赠) 3.22 Comments(过多的注释) 第4章 构筑测试体系 4.1 自测试代码的价值 4.2 JUnit测试框架 4.3 添加更多测试 第5章 重构列表 5.1 重构的记录格式 5.2 寻找引用点 5.3 这些重构手法有多成熟 第6章 重新组织函数 6.1 Extract Method(提炼函数) 6.2 Inline Method(内联函数) 6.3 Inline Temp(内联临时变量) 6.4 Replace Temp with Query(以查询取代临时变量) 6.5 Introduce Explaining Variable(引入解释性变量) 6.6 Split Temporary Variable(分解临时变量) 6.7 Remove Assignments to Parameters(移除对参数的赋值) 6.8 Replace Method with Method Object(以函数对象取代函数) 6.9 Substitute Algorithm(替换算法) 第7章 在对象之间搬移特性 7.1 Move Method(搬移函数) 7.2 Move Field(搬移字段) 7.3 Extract Class(提炼) 7.4 Inline Class(将内联化) 7.5 Hide Delegate(隐藏“委托关系”) 7.6 Remove Middle Man(移除间人) 7.7 Introduce Foreign Method(引入外加函数) 7.8 Introduce Local Extension(引入本地扩展) 第8章 重新组织数据 8.1 Self Encapsulate Field(自封装字段) 8.2 Replace Data Value with Object(以对象取代数据值) 8.3 Change Value to Reference(将值对象改为引用对象) 8.4 Change Reference to Value(将引用对象改为值对
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

每天进步一点_点

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值