重构手法之简化函数调用,重构手法之处理概括
分类:美高梅游戏官网网站

重构手法之简化函数调用【5】,重构手法简化函数

返回总目录

本小节目录

  • Hide Method(隐藏函数)
  • Replace Constructor with Factory Method(以工厂函数取代构造函数)

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

返回总目录

本小节目录

  • Pull Up Field(字段上移)
  • Pull Up Method(函数上移)
  • Pull Up Constructor Body(构造函数本体上移)

11Hide Method(隐藏函数)

1Pull Up Field(字段上移)

概要

有一个函数,从来没有被其他任何类用到。将这个函数设为private。

概要

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

动机

重构往往促使你修改函数的可见度。提高函数可见度的情况很容易想象:另一类需要用到某个函数,因此你必须提高该函数的可见度。

当你在另一个类中移除对某个函数的调用时,就应该检查有没有可能降低这个函数的可见度(使它私有化)。

动机

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

12Replace Constructor with Factory Method(以工厂函数取代构造函数)

范例

如下代码所示,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(函数上移)

范例:根据整数(实际是类型码)创建对象

class Employee
{
    public static int ENGINEER = 0;
    public static int SALESMAN = 1;
    public static int MANAGER = 2;
    private int _type;

    public Employee(int type)
    {
        _type = type;
    }
}

我希望为Employee提供不同的子类,并分别给予它们相应的类型码。因此,需要建立一个工厂函数:

public static Employee Create(int type)
{
    return new Employee(type);
}

然后,修改构造函数的所有调用点,让它们改用上述新建的工厂函数,并将构造函数声明为private:

Employee eng=Employee.Create(Employee.ENGINEER);

private Employee(int type)
{
    _type = type;
}

概要

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

范例:根据字符串创建子类对象

迄今为止,还没有获得什么实质收获。目前的好处在于:把“对象创建请求的接收者”和“被创建对象所属的类”分开了。随后把类型码转换为Employee的子类,就可以运用工厂函数,将这些子类对用户隐藏起来:

public static Employee Create(int type)
{
    switch (type)
    {
        case 0:
            return new Engineer();
        case 1:
            return new Salesman();
        case 2:
            return new Manager();
        default:
            throw new ArgumentException("Incorrect type code value.");
    }
}

可惜的是,这里有一个switch语句。如果添加一个新的子类,就必须记得更新这里的switch语句。

绕过这个switch语句的一个好办法是使用反射。第一件要做的就是修改参数类型。首先新建一个函数,接收一个字符串参数:

public static Employee Create(string name)
{
    try
    {
        var namespaceStr = MethodBase.GetCurrentMethod().DeclaringType.Namespace;
        var t = Type.GetType(namespaceStr + "." + name);
        return Activator.CreateInstance(t) as Employee;
    }
    catch (Exception ex)
    {
        throw new ArgumentException("unable to instantiate " + name);
    }
}

然后修改调用者,将下列这样的语句:

Employee eng = Employee.Create(Employee.ENGINEER);

修改为:

Employee eng = Employee.Create("Engineer");

美高梅网址,修改完后,可以将那个“Create()函数int版”移除了。

现在,当需要添加新的Employee子类时,就不需要再更新Create()函数了。但是却失去了编译期检验,使得一个小小的拼写错误就可能造成运行期错误。如果有必要防止运行期错误,则使用明确函数来创建对象(见下个例子)。但这样一来,每添加一个子类,就必须添加一个新函数。这就是为了类型安全而牺牲掉的灵活性。

动机

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

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

范例:以明确函数创建子类

如果只有少数几个子类,而且它们都不再变化,以明确函数隐藏子类很有用。假如有个Person类,它有两个子类:Male和Female。首先在基类中为每个子类定义一个工厂函数:

class Person
{
    public static Person CreateMale()
    {
        return new Male();
    }
    public static Person CreateFemale()
    {
        return new Female();
    }
}

然后,可以把下面的调用:

Person kent = new Male();

替换成:

Person kent = Person.CreateMale();

范例

以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;
    }
}

 小结

工厂函数是简单工厂模式的核心函数,实现了对象创建和使用的分离。

 

To Be Continued……

返回总目录 本小节目录 Hide Method(隐藏函数) Replace Constructor with Factory Method(以工厂函...

小结

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

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……

返回总目录 本小节目录 Pull Up Field(字段上移) Pull Up Method(函数上移) Pull Up Constru...

本文由美高梅网址发布于美高梅游戏官网网站,转载请注明出处:重构手法之简化函数调用,重构手法之处理概括

上一篇:Unity3D之物理射线,物理射线检测 下一篇:没有了
猜你喜欢
热门排行
精彩图文