• 重构手法之处理概括关系【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……

  • 相关阅读:
    根据年月日算出当前日期是星期几
    告别where 1=1 最佳方案分享
    服务器时间,剩余天、小时、分、秒倒计时代码
    分享一个Web弹框类
    从网上搜集了两篇关于数据库分页性能分析总结
    Request、Request.Form和Request.QueryString的区别
    LINQ to SQL语句之Join和Order By
    List转换成Json、对象集合转换Json等
    GRID控件删除之前确认
    LeetCode 141
  • 原文地址:https://www.cnblogs.com/liuyoung/p/7979074.html
Copyright © 2020-2023  润新知