第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)将针对原始类定义的所有外加函数搬移到扩展类中。

 

posted @ 2020-08-24 16:54  pmingup9012  阅读(185)  评论(0编辑  收藏  举报