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

Page Layout Max Width

Adjust the exact value of the page width of VitePress layout to adapt to different reading needs and screens.

Adjust the maximum width of the page layout
A ranged slider for user to choose and customize their desired width of the maximum width of the page layout can go.

Content Layout Max Width

Adjust the exact value of the document content width of VitePress layout to adapt to different reading needs and screens.

Adjust the maximum width of the content layout
A ranged slider for user to choose and customize their desired width of the maximum width of the content layout can go.