前言
关于工厂的一些概念,我在1,2两节中有提及,这里不过多数赘述,详情参考下面两篇博客。
设计模式学习[1]—开篇词+简单工厂模式
设计模式学习[2]—策略模式+简单工厂回顾
1.原理阐述
工厂方法模式(Factory Method)
:定义一个用于创建对象的借口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。
工厂方法模式和前面简单工厂又有什么区别呢?
简单工厂中,我们对于导出操作,根据导出数据的类型不同,导出的方法也不同,可以分为导出图片和导出文本等等。
那么如果现在有一个新的需求,叫导出表格。
对于简单工厂来说,我们需要在下面的代码中的switch语句中添加分支,case table:{XXXX};
class ExportOperation
{
public:
virtual void exportData() {};
};
class ExportText :public ExportOperation
{
public:
void exportData() override;
};
class ExportImage :public ExportOperation
{
public:
void exportData() override;
};
enum OperationType
{
text = 1,
image
};
class ExportFactory
{
public:
static ExportOperation* createExportOperation(OperationType type)
{
ExportOperation* Operation;
switch (type)
{
case text:
{
Operation = new ExportText();
break;
}
case image:
{
Operation = new ExportImage();
break;
}
default:
return nullptr;
}
};
};
int main()
{
ExportOperation* m_pexOp = ExportFactory::createExportOperation(OperationType::text);
m_pexOp->exportData();
return 0;
}
我们这是在做什么?是不是对这个类进行了修改?对于一个已经设计好的类,我们最好的情况是什么,是修改类本身吗?当然不是,应该是进行拓展。这是开放与封闭原则
的体现。
说到这的意思就是,工厂方法模式和简单工厂模式的区别了,是否满足开放与封闭原则。
下面是书中的原话
这其实就是工厂方法模式和简单工厂的区别所在。简单工厂模式的最大优点在于工厂类中包含了必要的逻辑判断,根据客户端的选择条件动态实例化相关的类,对于客户端来说,去除了与具体产品的依赖。 就像你的计算器,让客户端不用管该用哪个类的实例,只需要把‘+’给工厂,工厂自动就给出了相应的实例,客户端只要去做运算就可以了,不同的实例会实现不同的运算。但问题也就在这里,如你所说,如果要加一个‘求M数的N次方’的功能,我们是一定需要给运算工厂类的方法里加‘Case’的分支条件的,修改原有的类?这可不是好办法,这就等于说,我们不但对扩展开放了,对修改也开放了,这样就违背开放与封闭原则。
2.举例
基于之前的导出操作相关的类图,我们进行修改
原来的类图:
工厂方法模式类图:
对比一下,我在ExportFactory这个类中派生了两个工厂类用来处理特定的操作类。
在创建工程的时候,我只需要创建对应的操作工程即可。比如我导出文本只需要创建TextFactory对象即可。
这样ExportFactory中就不需要Switch了,那这种要声明什么对象的判断实际上是交给了客户端。
#include<iostream>
using namespace std;
class ExportOperation
{
public:
virtual void exportData() {};
};
class ExportText :public ExportOperation
{
public:
void exportData() { cout << "导出文本" << endl; };
};
class ExportImage :public ExportOperation
{
public:
void exportData() { cout << "导出图片" << endl; };
};
class ExportFactory
{
public:
virtual ExportOperation* createExportOperation(){return nullptr;};
};
class TextFactory :public ExportFactory
{
public:
virtual ExportOperation* createExportOperation() { return new ExportText(); };
};
class ImageFactory :public ExportFactory
{
public:
virtual ExportOperation* createExportOperation() { return new ExportImage(); };
};
int main()
{
//导出文本
ExportFactory* m_pExportText = new TextFactory();
ExportOperation* m_pTextOp = m_pExportText->createExportOperation();
m_pTextOp->exportData();
//导出图片
ExportFactory* m_pExportImage = new ImageFactory();
ExportOperation* m_pImageOp = m_pExportImage->createExportOperation();
m_pImageOp->exportData();
return 0;
}
现在考虑增加一个导出表格,怎么改?
增加需求,是不是拓展。那现有的类需要动吗?不需要,因为我们这些类的内部实现和新增的需求对应的类并没有太大的关系。
我们只需要新增一个ExportTable类和一个TableFacotry类。在客户端里面,什么类型之类的,交给客户端即可,我们底层不关心。
工厂类与分支耦合,那么我就对它下手,根据依赖倒转原则,我们把工厂类抽象出一个接口,这个接口只有一个方法,就是创建抽象产品的工厂方法。然后,所有的要生产具体类的工厂,就去实现这个接口,这样,一个简单工厂模式的工厂类,变成了一个工厂抽象接口和多个具体生成对象的工厂,于是我们要增加一些功能时,就不需要更改原有的工厂类了,只需要增加此功能的操作类和相应的工厂类就可以了。”
总结
我们通过工厂方法模式,将不同类型对象的实例化直接交给客户端,从而让底层的代码解耦,满足开放-封闭原则。
最近几个模式好像都是涉及到了开放-封闭原则,对于各种原则秉持一个想法:多用,多思考。