Fork me on GitHub

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

返回总目录

本小节目录

1Pull Up Field(字段上移)

概要

两个子类拥有相同的字段。将该字段移至基类。

动机

如果各个子类是分别开发的,或者是在重构过程中组合起来的,你常常会发现它们拥有重复性,特别是字段容易重复。判断若干字段是否重复,唯一的办法就是观察函数如何使用它们。如果它们被使用的方式很相似,就可以将它们归纳到基类中去。

范例

如下代码所示,Employee的两个子类Salesman和Enginner都有_name字段,所以可以考虑把这个字段提到基类中。

class Employee
{

}

class Salesman : Employee
{
    private string _name;
}

class Enginner : Employee
{
    private string _name;
}

重构后的代码如下,这样提的前提是这些子类有一个基类或者有很多相似的字段和方法,不然为了一个字段而单独建立一个抽象类是不可取的,所以这个就需要具体权衡。

class Employee
{
    protected string _name;
}

class Salesman : Employee
{
 
}

class Enginner : Employee
{

}

小结

本项重构主要是减少重复:首先它去除了重复的数据声明;其次它使你可以将该字段的行为从子类移至基类,从而去除重复的行为。

2Pull Up Method(函数上移)

概要

有些函数,在各个子类中产生完全相同的结果。将该函数移至基类。

动机

避免行为重复是很重要的。尽管重复的两个函数也可以各自工作得很好,但重复自身只会成为错误的滋生地,此外别无价值。无论何时,只要系统中出现重复,你就面临“修改其中一个却未能修改另一个”的风险。

使用本项重构的场合:(1)如果某个函数在各个子类中的函数体都相同;(2)子类的函数覆写了基类的函数,但却仍然做相同的工作。

范例

以Customer表示“顾客”,它有两个子类:表示“普通顾客”的RegularCustomer和表示“贵宾”的PreferredCustomer。

public abstract class Customer
{
    protected DateTime _lastBillDate;
    public void AddBill(DateTime date, double amount)
    {

    }
}

class RegularCustomer : Customer
{
    void CreateBill(DateTime date)
    {
        double chargeAmount = ChargeFor(_lastBillDate, date);
        AddBill(date, chargeAmount);
    }

    public double ChargeFor(DateTime start, DateTime end)
    {
        return 0;
    }
}

class PreferredCustomer : Customer
{
    void CreateBill(DateTime date)
    {
        double chargeAmount = ChargeFor(_lastBillDate, date);
        AddBill(date, chargeAmount);
    }

    public double ChargeFor(DateTime start, DateTime end)
    {
        return 100;
    }
}

两个子类中都有一个CreateBill()函数,并且代码完全一样,但我不能直接把这个函数上移到基类中,因为各个子类的ChargeFor()函数并不相同。必须先在基类中声明一个ChargeFor()抽象函数:

public abstract class Customer
{
    protected DateTime _lastBillDate;
    protected void AddBill(DateTime date, double amount)
    {

    }
    protected void CreateBill(DateTime date)
    {
        double chargeAmount = ChargeFor(_lastBillDate, date);
        AddBill(date, chargeAmount);
    }
    public abstract double ChargeFor(DateTime start, DateTime end);
}

class RegularCustomer : Customer
{
    public override double ChargeFor(DateTime start, DateTime end)
    {
        return 0;
    }
}

class PreferredCustomer : Customer
{
    public override double ChargeFor(DateTime start, DateTime end)
    {
        return 100;
    }
}

小结

这个重构要根据具体情况使用,如果不是每个子类都有这个方法的话,可以考虑使用接口或者其他方式。

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

概要

你在各个子类中拥有一些构造函数,它们的本体几乎完全一致。

在基类中新建一个构造函数,并在子类构造函数中调用它。

动机

如果你看见各个子类中的函数有共同行为,第一个念头应该是将共同行为提炼到一个独立函数中,然后将这个函数提升到基类。对于构造函数而言,它们彼此的共同行为往往就是“对象的建构”。这时候你需要在基类中提供一个构造函数,然后让子类都来调用它。

范例

class Employee
{
    protected string _name;

    protected string _id;

}

class Manager:Employee
{
    private int _grade;
    public Manager(string name,string id,int grade)
    {
        _name = name;
        _id = id;
        _grade = grade;
    }
}

Employee的字段应该在Employee构造函数中设值。因此定义了一个Employee构造函数,并将它声明为Protected,表示子类应该调用它:

class Employee
{
    protected string _name;

    protected string _id;

    protected Employee(string name, string id)
    {
        _name = name;
        _id = id;
    }
}

class Manager : Employee
{
    private int _grade;
    public Manager(string name, string id, int grade) : base(name, id)
    {
        _grade = grade;
    }
}

小结

这个重构手法和提升字段、提升方法很相似。只不过是将“对象的建构”提升到基类中。

 

To Be Continued……

posted @ 2017-12-06 08:57  NaYoung  阅读(825)  评论(0编辑  收藏  举报