Skip to content

重构 - 12. 处理继承关系

🏷️ 《重构》

12.1 函数上移(Pull Up Method)

反向重构函数下移(Push Down Method)

避免重复代码是很重要的。

重构前

csharp
class Employee
{
}

class Salesman : Employee
{
    public void Rest() { }
}

class Engineer : Employee
{
    public void Rest() { }
}

重构后

csharp
class Employee
{
    public void Rest() { }
}

class Salesman : Employee
{
}

class Engineer : Employee
{
}

12.2 字段上移(Pull Up Field)

反向重构字段下移(Push Down Field)

函数上移类似。

重构前

csharp
class Employee
{
}

class Salesman : Employee
{
    public string Name { get; set; }
}

class Engineer : Employee
{
    public string Name { get; set; }
}

重构后

csharp
class Employee
{
    public string Name { get; set; }
}

class Salesman : Employee
{
}

class Engineer : Employee
{
}

12.3 构造函数本体上移(Pull Up Constructor Body)

重构前

csharp
class Party
{
}

class Employee : Party
{
    public Employee(int id, string name, decimal monthlyCost)
    {
        Id = id;
        Name = name;
        MonthlyCost = monthlyCost;
    }

    public int Id { get; set; }
    public string Name { get; set; }
    public decimal MonthlyCost { get; set; }
}

重构后

csharp
class Party
{
    public Party(string name)
    {
        Name = name;
    }

    public string Name { get; set; }
}

class Employee : Party
{
    public Employee(int id, string name, decimal monthlyCost) : base(name)
    {
        Id = id;
        MonthlyCost = monthlyCost;
    }

    public int Id { get; set; }
    public decimal MonthlyCost { get; set; }
}

12.4 函数下移(Push Down Method)

反向重构函数上移(Pull Up Method)

重构前

csharp
class Employee
{
    private int _quota;

    public int Quota()
    {
        return _quota;
    }
}

class Engineer : Employee
{
}

class Salesman : Employee
{
}

重构后

csharp
class Employee
{
}

class Engineer : Employee
{
}

class Salesman : Employee
{
    private int _quota;

    public int Quota()
    {
        return _quota;
    }
}

12.5 字段下移(Push Down Field)

反向重构字段上移(Pull Up Field)

重构前

csharp
class Employee
{
    public int Quota { get; set; }
}

class Engineer : Employee
{
}

class Salesman : Employee
{
}

重构后

csharp
class Employee
{
}

class Engineer : Employee
{
}

class Salesman : Employee
{
    public int Quota { get; set; }
}

12.6 以子类取代类型码(Replace Type Code with Subclasses)

包含旧重构以 State/Strategy 取代类型码(Replace Type Code with State/Strategy)
包含旧重构提炼子类(Extract Subclass)
反向重构移除子类(Remove Subclass)

重构前

csharp
Employee CreateEmployee(string name, string type)
{
    return new Employee(name, type);
}

重构后

csharp
Employee CreateEmployee(string name, string type)
{
    switch (type)
    {
        case "engineer":
            return new Engineer(name);
        case "saleman":
            return new Saleman(name);
        case "manager":
            return new Manager(name);
        default:
            return new Employee(name);
    }
}

12.7 移除子类(Remove Subclass)

曾用名以字段取代子类(Replace Subclass with Fields)
反向重构以子类取代类型码(Replace Type Code with Subclasses)

子类存在着就有成本,所以如果子类的用处太少,就不值得存在了。

重构前

csharp
class Person
{
    public virtual string GenderCode => "X";
}

class Male : Person
{
    public override string GenderCode => "M";
}

class Female : Person
{
    public override string GenderCode => "F";
}

重构后

csharp
class Person
{
    public string GenderCode { get; set; }
}

12.8 提炼超类(Extract Superclass)

如果看见两个在做相似的事,可以利用基本的继承机制把他们的相似之处提炼到超类。

重构前

csharp
class Department
{
    public string Name { get; set; }
    public int HeadCount { get; set; }
    public decimal MonthlyCost { get; set; }
    public decimal TotalAnnualCost()
    {
        return decimal.Multiply(decimal.Multiply(MonthlyCost, new decimal(12)), new decimal(1.2));
    }
}

class Employee
{
    public string Name { get; set; }
    public int Id { get; set; }
    public decimal MonthlyCost { get; set; }
    public decimal AnnualCost()
    {
        return decimal.Multiply(MonthlyCost, new decimal(14));
    }
}

重构后

csharp
class Party
{
    public string Name { get; set; }
    public decimal MonthlyCost { get; set; }
    public virtual decimal AnnualCost()
    {
        return decimal.Multiply(MonthlyCost, new decimal(12));
    }
}

class Department : Party
{
    public int HeadCount { get; set; }
    public override decimal AnnualCost()
    {
        return decimal.Multiply(base.AnnualCost(), new decimal(1.2));
    }
}

class Employee : Party
{
    public int Id { get; set; }
    public override decimal AnnualCost()
    {
        return decimal.Add(base.AnnualCost(), decimal.Multiply(MonthlyCost, new decimal(2)));
    }
}

12.9 折叠继承体系(Collapse Hierarchy)

若一个类与其超类已经没多大差别,则其不值得再作为独立的类存在。

重构前

csharp
class Employee { }
class Saleman : Employee { }

重构后

csharp
class Employee { }

12.10 以委托取代子类(Replace Subclass with Delegate)

继承是个很强大的机制,但也有其短板:大多数语言只允许但继承;继承给类之间引入了非常紧密的联系。

使用委托可以解决上述问题。

“对象组合优于类继承”(“组合”跟“委托”是同一回事)。

重构前

csharp
class Order
{
    private Warehouse _warehouse;

    public virtual int DaysToShip
    {
        get
        {
            return _warehouse.DaysToShip;
        }
    }
}

class PriorityOrder : Order
{
    private PriorityPlan _priorityPlan;

    public override int DaysToShip
    {
        get
        {
            return _priorityPlan.DaysToShip;
        }
    }
}

重构后

csharp
class Order
{
    private Warehouse _warehouse;
    private PriorityPlanDelegate _piorityPlanDelegate;

    public int DaysToShip
    {
        get
        {
            return _piorityPlanDelegate != null
                ? _piorityPlanDelegate.DaysToShip
                : _warehouse.DaysToShip;
        }
    }
}

class PriorityPlanDelegate
{
    private PriorityPlan _priorityPlan;

    public int DaysToShip
    {
        get
        {
            return _priorityPlan.DaysToShip;
        }
    }
}

12.11 以委托取代超类(Replace Superclass with Delegate)

曾用名以委托取代继承(Replace Inheritance with Delegate)

如果超类的一些函数对于子类并不适用,就说明不应该通过继承来获得超类的功能。

合理的继承关系还有一个重要特征:子类的所有实例都应该是超类的实例,通过超类的接口来使用子类的实例应该完全不出问题。

首先(尽量)使用继承,如果发现继承有问题,再使用 以委托取代超类

重构前

csharp
class List
{
    // ...
}

class Stack : List
{
    // ...
}

重构后

csharp
class List
{
    // ...
}

class Stack
{
    List _stoarge;

    public Stack()
    {
        _stoarge = new List();
    }

    // ...
}

附 1. 引用

  1. 《重构:改善既有代码的设计》 -- 马丁·福勒(Martin Fowler