Fork me on GitHub

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

返回总目录

本小节目录

11Hide Method(隐藏函数)

概要

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

动机

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

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

12Replace Constructor with Factory 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()函数了。但是却失去了编译期检验,使得一个小小的拼写错误就可能造成运行期错误。如果有必要防止运行期错误,则使用明确函数来创建对象(见下个例子)。但这样一来,每添加一个子类,就必须添加一个新函数。这就是为了类型安全而牺牲掉的灵活性。

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

如果只有少数几个子类,而且它们都不再变化,以明确函数隐藏子类很有用。假如有个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();

 小结

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

 

To Be Continued……

posted @ 2017-12-04 08:53  NaYoung  阅读(873)  评论(0编辑  收藏  举报