问题描述
在面向对象系统设计中经常可以遇到以下的两类问题:
1)为了提高内聚(Cohesion)和松耦合(Coupling),我们经常会抽象出一些类的公共接口以形成抽象基类或者接口。这样我们可以通过声明一个指向基类的指针来指向实际的子类实现, 达到了多态的目的。
这里很容易出现的一个问题 n 多的子类继承自抽象基类, 我们不得不在每次要用到子类的地方就编写诸如 new ×××;的代码。这里带来两个问题:
->1.客户程序员必须知道实际子类的名称(当系统复杂后, 命名将是一个很不好处理的问题, 为了处理可能的名字冲突, 有的命名可能并不是具有很好的可读性和可记忆性, 就姑且不论不同程序员千奇百怪的个人偏好了。)
->2.程序的扩展性和维护变得越来越困难。
2)还有一种情况就是在父类中并不知道具体要实例化哪一个具体的子类。
假设我们在类 A 中要使用到类 B, B 是一个抽象父类,在 A 中并不知道具体要实例化那一个 B 的子类,但是在类A的子类D中是可以知道的。在A中我们没有办法直接使用类似于new×××的语句,因为根本就不知道×××是什么。
以上两个问题也就引出了 Factory 模式的两个最重要的功能:
1)定义创建对象的接口,封装了对象的创建;
2)使得具体化类的工作延迟到了子类中。
在第一个问题中,我们经常就是声明一个创建对象的接口,并封装了对象的创建过程。 Factory 这里类似于一个真正意义上的工厂(生产对象)。
在第二个问题中,我们需要提供一个对象创建对象的接口,并在子类中提供其具体实现(因为只有在子类中可以决定到底实例化哪一个)。
UML类图
对于工厂模式,具体上可以分为三类:
- 简单工厂模式;
- 工厂方法模式;
- 抽象工厂模式。
对于上面的三种工厂模式,从上到下逐步抽象,并且更具一般性。本篇主要论述第一类(简单工厂模式)和第二类(工厂方法模式)。
第一种情况下(对应的就是“简单工厂模式”):
上图所示的Factory模式经常在系统开发中用到,但是这并不是 Factory模式的最大威力所在(因为这可以通过其他方式解决这个问题)。Factory模式不单是提供了创建对象的接口,其最重要的是延迟了子类的实例化(第二个问题)。
如下图所示(对应的就是工厂方法模式):
这种模式的应用并不是只是为了封装对象的创建,而是要把对象的创建放到子类中实现: Factory 中只是提供了对象创建的接口,其实现将放在 Factory 的子类ConcreteFactory 中进行。
适用场合
简单工厂模式:
- 在程序中,需要创建的对象很多,导致对象的new操作多且杂时,需要使用简单工厂模式;
- 由于对象的创建过程是我们不需要去关心的,而我们注重的是对象的实际操作,所以,我们需要分离对象的创建和操作两部分,如此,方便后期的程序扩展和维护。
工厂方法模式:
工厂方法模式的意义是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类当中。它修正了简单工厂模式中不遵守开放—封闭原则。核心工厂类不再负责产品的创建,这样核心类成为一个抽象工厂角色,仅负责具体工厂子类必须实现的接口,这样进一步抽象化的好处是使得工厂方法模式可以使系统在不修改具体工厂角色的情况下引进新的产品。
- 在设计的初期,就考虑到产品在后期会进行扩展的情况下,可以使用工厂方法模式;
- 产品结构较复杂的情况下,可以使用工厂方法模式;
由于使用设计模式是在详细设计时,就需要进行定夺的,所以,需要权衡多方面的因素,而不能为了使用设计模式而使用设计模式。
代码实现
简单工厂模式:
#include <iostream> #include <string> using namespace std; //基类 class Operation { public: double numberA, numberB; virtual double getResult() { return 0; } }; //加法 class addOperation :public Operation { double getResult() { return numberA + numberB; } }; //减法 class subOperation :public Operation { double getResult() { return numberA - numberB; } }; //乘法 class mulOperation :public Operation { double getResult() { return numberA*numberB; } }; //除法 class divOperation :public Operation { double getResult() { return numberA / numberB; } }; //工厂类 class operFactory { public: static Operation *createOperation(char c) {//在c#中可以用反射来取消判断时用的switch,那么c++中用的是啥呢?RTTI? switch (c) { case ‘+‘: return new addOperation; break; case ‘-‘: return new subOperation; break; case ‘*‘: return new mulOperation; break; case ‘/‘: return new divOperation; break; } } }; int main() { Operation *oper = operFactory::createOperation(‘+‘); oper->numberA = 9; oper->numberB = 99; cout << oper->getResult() << endl; return 0; }
运行结果:
工厂方法模式:
#include <iostream> #include <string> using namespace std; class Operation { public: double numberA, numberB; virtual double getResult() { return 0; } }; class addOperation :public Operation { double getResult() { return numberA + numberB; } }; class subOperation :public Operation { double getResult() { return numberA - numberB; } }; class mulOperation :public Operation { double getResult() { return numberA*numberB; } }; class divOperation :public Operation { double getResult() { return numberA / numberB; } }; class IFactory { public: virtual Operation *createOperation() = 0; }; class AddFactory :public IFactory { public: static Operation *createOperation() { return new addOperation(); } }; class SubFactory :public IFactory { public: static Operation *createOperation() { return new subOperation(); } }; class MulFactory :public IFactory { public: static Operation *createOperation() { return new mulOperation(); } }; class DivFactory :public IFactory { public: static Operation *createOperation() { return new divOperation(); } }; int main() { Operation *oper = MulFactory::createOperation(); oper->numberA = 9; oper->numberB = 99; cout << oper->getResult() << endl; return 0; }
运行结果:
---------------------------------------------------------
#include <iostream> #include <string> using namespace std; //实例基类,相当于Product(为了方便,没用抽象) class LeiFeng { public: virtual void sweep() { cout << "雷锋扫地" << endl; } void wash() { cout << "雷锋洗衣服" << endl; } }; //学雷锋的大学生,相当于ConcreteProduct class Student :public LeiFeng { public: virtual void sweep(){ cout << "大学生扫地" << endl; } }; //学雷锋的志愿者,相当于ConcreteProduct class Volunteer :public LeiFeng { public: virtual void sweep(){ cout << "zhiyaunzhe" << endl; } }; //工厂基类Creator class LeiFengFactory { public: virtual LeiFeng *createLeiFeng() { return new LeiFeng(); } }; //工厂具体类 class StudentFactory :public LeiFengFactory { public: virtual LeiFeng *createLeiFeng() { return new Student(); } }; class VolFactory :public LeiFengFactory { public: virtual LeiFeng *createLeiFeng() { return new Volunteer(); } }; int main() { LeiFengFactory *sf = new LeiFengFactory(); LeiFeng *s = sf->createLeiFeng(); s->sweep(); delete s; delete sf; return 0; }
运行结果:
参考文献:
《大话设计模式 C++》
《C++设计模式》
另外可参考博客(其系列写得不错):C++设计模式——简单工厂模式