Fork me on GitHub

重构手法之处理概括关系【5】

返回总目录

本小节目录

11Replace Inheritance with Delegation(以委托取代继承)

概要

某个子类只使用基类接口中的一部分,或是根本不需要继承而来的数据。

在子类中新建一个字段用以保存基类;调整子类函数,令它改而委托基类;然后去掉两者之间的继承体系。

动机

使用继承时常常会遇到这样的问题:一开始继承了一个类,随后发现基类中的许多操作并不真正适用于子类。这种情况下,你所拥有的接口并未真正反映出子类的功能。或者,你可能发现你从基类中继承了一大堆子类并不需要的数据,抑或你可能发现基类中的某些protected函数对子类并没有意义。

如果以委托取代继承,可以更清楚地表明:你只需要受托类的一部分功能。接口中的哪一部分应该被使用,哪一部分应该被忽略,完全由你主导控制。这样做的成本则是需要额外写出委托函数,但这些函数都非常简单。

范例

如下代码所示,Child和Sanitation(公共设施)是没有逻辑上的父子关系,因为小孩不可能是一个公共设施吧!所以我们为了完成这个功能可以考虑使用委派的方式。

public class Sanitation
{
    public string WashHands()
    {
        return "Cleaned!";
    }
}

public class Child : Sanitation
{
}

重构后的代码如下,把Sanitation委派到Child类中,从而可以使用WashHands这个方法,这种方式我们经常会用到,其实IOC也使用到了这个原理,可以通过构造注入和方法注入等。

public class Sanitation
{
    public string WashHands()
    {
        return "Cleaned!";
    }
}

public class Child
{
    private Sanitation _sanitation;

    public Child()
    {
        _sanitation = new Sanitation();
    }

    public string WashHands()
    {
        return _sanitation.WashHands();
    }
}

小结

这个重构是一个很好的重构,在很大程度上解决了滥用继承的情况,很多设计模式也用到了这种思想(比如桥接模式、适配器模式、策略模式等)。

12Replace Delegation with Inheritance(以继承取代委托)

概要

你在两个类之间使用委托关系,并经常为整个接口编写许多极简单的委托函数。让委托类继承受托类。

动机

如果你发现自己需要受托类中的所有函数,并且花费很大力气编写所有极简单的委托函数,本重构可以帮助你轻松回头使用继承。

范例

下面是一个简单的Employee类,将一些函数委托给另一个同样简单的Person类:

class Employee
{
    private Person _person = new Person();

    public override string ToString()
    {
        return "Emp: " + _person.GetLastName();
    }
}

class Person
{
    public string Name { get; set; }

    public string GetLastName()
    {
        return Name.Substring(Name.LastIndexOf(' ') + 1);
    }
}

重构后代码如下:

class Employee : Person
{
    public override string ToString()
    {
        return "Emp: " + GetLastName();
    }
}

class Person
{
    public string Name { get; set; }

    public string GetLastName()
    {
        return Name.Substring(Name.LastIndexOf(' ') + 1);
    }
}

小结

如果并没有使用受托类的所有函数,那么就不应该使用本重构,因为子类应该总是遵循基类的接口。

受托对象被不止一个其他对象共享,而且受托对象是可变的。在这种情况下,就不能将委托关系替换为继承关系,因为这样就无法再共享数据了。

阶段性小结

重构手法之处理概括关系【1】-【5】,顾名思义,专门用来处理类的概括关系即继承关系。

其中主要是将函数上下移动于继承体系中。Pull Up FieldPull Up Method都用于将特性向继承体系的上端移动,Push Down MethodPush Down Field则将特性向继承体系的下端移动。构造函数比较难以向上拉动,因此专门有一个Pull Up Constructor Body处理它。一般不会将构造函数往下推,因为Replace Constructor with Factory Method通常更管用。

如果有若干函数大体上相同,只在细节上有所差异,可以使用Form Template Method将它们的共同点和不同点分开。

除了在继承体系中移动特性外,还可以建立新类,改变整个继承体系。Extract SubclassExtract BaseClassExtract Interface都是这样的重构手法,它们在继承体系中的不同位置构造出新元素。如果想在类型系统中标示一小部分函数,Extract Interface特别有用。如果发现继承体系中的某些类没有存在的必要,可以使用Collapse Hierarchy将它们移除。

有时候会发现继承并非是最佳选择,真正需要的其实是委托,那么Replace Inheritance with Delegation可以帮助把继承改为委托。有时候又会想要做反向修改,此时就可以使用Replace Delegation with Inheritance

 

To Be Continued……

posted @ 2017-12-12 13:10  NaYoung  阅读(758)  评论(3编辑  收藏  举报