设计模式详解:Factory Method(工厂方法)

Factory Method 工厂方法

设计模式学习:概述

意图

定义一个用于创建对象的接口,让子类决定实例化哪个类。Factory Method使得一个类的实例化延迟到其子类。

Factory Method又称作Virtual Constructor(虚构造器)。

在软件系统中,经常面临着创建对象的工作,而需求的变化常常导致所需要创建的对象的具体类型发生变化。

Factory Method使得可以绕过常规的对象创建方法(new关键字),将封装机制引入到对象的创建工作中,从而避免了客户程序与“具体对象创建工作”产生紧耦合。

不过要注意,不可能做到完全绕过new进行对象创建,我们想要做的,是将创建对象的职责向上转移,从而是子程序具有更高的灵活性。

代码案例

假如,现在有若干类,每一个子类代表一种动物:

class Animal{
    
    
    virtual void shout() = 0;
}

class Rabbit: public Anlmal{
    
    
    void shout(){
    
    cout << "ZI";}
}
class Cat: public Anlmal{
    
    
    void shout(){
    
    cout << "MIAO";}
}
class Dog: public Anlmal{
    
    
    void shout(){
    
    cout << "WANG";}
}

我们在函数中这样调用它:

void func()
{
    
    
    Animal* an = new Rabbit();
    an->shout();
}

如上所示,我们创建了一个兔子对象,并调用了它的Shout方法。

乍一看,其实这段代码并没有什么不妥的地方,但它实际上违反了一个重要的设计原则:依赖倒置原则,详细来说,就是**基类对象的创建依赖于子类的存在。**也就是说,在这段代码中,class Rabbit必须存在来使得代码顺利编译通过。

听起来不是什么大事?举个例子,在团队合作的项目中,这就要求编写函数的人必须拥有一个子类(大概率不是他本人负责编写的类)的副本,扩大来说,团队中的每一个人都必须拥有这个基类每个子类的副本。在子类被更新时,每个人都必须更新自己手上的类文件。

或许如果你学过编译原理的话,用静态链接和动态共享的类比会更好理解:这种方法白白占用了更多的内存。更重要的是,它为项目的维护带来了不便。下面我会对这一点做更详细的解释。

创建类设计模式的产生,就是为了将对象的创建作为一个单独的模块维护,从而降低系统维护难度而产生,现在,我们来看看FactoryMethod怎样解决这个问题。

解决方案:

首先创建这样一个类:

class AnimalFactory
{
	virtual Animal* CreateAnimal() = 0;
}

接着,为每一个存在的Animal子类编写对应的工厂子类。

class RabbitFactory: AnimalFactory{
	Animal* CreateAnimal(){
		return new Rabbit();
	};
}

class CatFactory: AnimalFactory{
	Animal* CreateAnimal(){
		return new Cat();
	};
}

class DogFactory: AnimalFactory{
	Animal* CreateAnimal(){
		return new Dog();
	};
}

这样,我们就可以用工厂来代替New进行对象创建了。为了满足依赖倒置原则的要求,我们用这种方式来指定工厂的种类:

class solution
{
    
    
    AnimalFactory* af;
    solution(AnimalFactory* p): af(p){
    
     }
    void func()
    {
    
    
        Animal* an = af.CreateAnimal();
        an.shout();
    }
}

这样一来,solution类与Animal某一子类的紧耦合关系就被解除了,只需要创建solution对象时指定它对应的工厂即可。没错,这又是我们在设计模式学习中常见的技巧:编译时多态转运行时多态

看上去,我们只是把对象创建延迟到了上一级,有点多此一举的意味。实际上,这对程序的封装性是很大的改善。编写solution类的程序猿,完全不需要知道子类是什么,团队又可以愉快地一起工作了。

解释

事实上,创建对象型设计模式的原理都很类似(它们包括FactoryMethod、AbstractFactory, 而Builder不同点多一些)。都是将对象创建的过程作为一个单独模块封装起来,从而降低程序内部的耦合性。

FactoryMethod主要遵循了依赖倒置原则,它使得编写基类相关的方法时不需要依赖特定的某个子类。

关于这一点,我在这里用这样一个例子来说明:你在考驾照时,不关心将来自己具体要开哪种车,只关心开的是手动挡、自动挡、小轿车还是大货这种问题。不使用FactoryMethod的话,就好像你在学驾照的时候,就要清楚将来要开桑塔纳还是别克,这就是一种依赖倒置。

再以上文为例:既然func函数与子类是什么没有关系,那么就不应当要求存在某一特定的子类使得func函数编译通过,甚至不存在任何一种子类时,它也应当正常编译(相当于纸上谈兵)

总结

设计模式 Factory Method (工厂方法)
稳定点: 工厂产品的参数(每种子类创建时需要的参数类型必须相同)
变化点: 工厂的产品种类(子类类型)
效果: 编写操作基类的函数时不需要对其具体子类有任何的了解
特点: 将对象创建作为单独的模块封装起来

类图:

类图:来自GOF《设计模式》
2021.2.9 转载请标明出处

猜你喜欢

转载自blog.csdn.net/natrick/article/details/113767700