Fork me on GitHub

重构手法之在对象之间搬移特性【3】

返回总目录

本小节目录

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

概要

客户通过一个委托关系来调用另一个对象。

在服务类上建立客户所需的所有函数,用以隐藏委托关系。

动机

如果某个客户先通过服务对象的字段或者属性得到另一个对象,然后调用后者的函数,那么客户就必须知晓这一层委托关系。万一委托关系发生变化,客户也得相应变化。你可以在服务对象上放置一个简单的委托关系,将委托关系隐藏起来,从而去除这种依赖。这么一来,即便将来发生委托关系上的变化,变化也将被限制在服务对象中,不会波及客户。

范例

看下面代表“人”的Person类和代表“部门”的Department类:

class Person
{
    public Department Department { get; set; }

}
class Department
{
    private string _chargeCode;

    public Person Manager { get; set; }

    public Department(Person manager)
    {
        Manager = manager;
    }

}

如果客户想知道某人的经理是谁,他必须先取得Department对象:

Person john = new Person();
var manager = john.Department.Manager;

这样的编码就对客户揭露了Department的工作原理,于是客户知道:Department用以追踪“经理”这条消息。如果对客户隐藏Department,可以减少耦合。为此,我们在Person中建立一个简单的委托函数:

class Person
{
    public Department Department { get; set; }

    public Person GetManager()
    {
        return Department.Manager;
    }
}

现在,我得修改Person的所有客户,让它们改用新函数:

Person john = new Person();
var manager = john.GetManager();

修改完所有的客户之后,就可以将Person的Department的get设为私有的了。

public Department Department { private get; set; }

当然了也可以将属性改为私有字段:

private Department _department;

小结

“封装”即使不是对象的最关键特征,也是最关键特征之一。“封装”意味着对象都应该尽可能少了解系统的其他部分。如此一来,一旦发生变化,需要了解这一变化的对象就会较少。

6Remove Middle Man(移除中间人)

概要

某个类做了过多的简单委托动作。

让客户直接调用委托类。

动机

第5节我们说到“封装受托对象”的好处。但是这样的封装也是要付出代价的,即:每当客户要使用受托类的新特性时,就必须在服务端添加一个简单委托函数。随着受托类的特性越来越多,这一过程将非常痛苦。服务类完全成了“中间人”,这时候就需要让客户直接调用受托类。

范例

还是来看上面的“人与部门”的例子。

class Person
{
    public Department Department { private get; set; }

    public Person GetManager()
    {
        return Department.Manager;
    }
}
class Department
{
    private string _chargeCode;

    public Person Manager { get; set; }

    public Department(Person manager)
    {
        Manager = manager;
    }

}

为了找出某人的经理,客户代码可能这样写:

Person john = new Person();
var manager = john.GetManager();

像这样,使用封装和Department都很简单。但如果大量函数都这么做,就不得不在Person之间安置大量的委托行为。这就是该移除中间人的时候了。

首先在Person中建立一个函数用于获得受托对象:

public Department GetDepartment()
{
    return Department;
}

然后逐一处理每个委托函数。针对每一个这样的函数,找出通过Person使用的函数,并对它进行修改,使它首先获得受托对象,然后直接使用后者:

var manager = john.GetDepartment().Manager;

然后就可以删除Person的GetManager()函数。

小结

很难说什么程度的隐藏才是最合适的。随着系统的变化,“合适的隐藏程度”这个尺度也在相应改变。昨天你感觉恰如其分的封装,今天就可能显得很笨拙。还好,我们有了Hide Delegate和Remove Middle Man,因为我们可以在系统运行过程中不断进行调整。

重构的意义在于:永远不必说对不起——只要把出问题的地方修补好就行了。

 

To Be Continued……

posted @ 2017-11-24 09:07  NaYoung  阅读(737)  评论(3编辑  收藏  举报