简单工厂模式的介绍
那么如何来理解什么是简单工厂模式?
==================================================分割线=========================================
首先来看看百度给的解释吧
简单工厂模式的实质是由一个工厂类根据传入的参数的不同,动态决定应该创建哪一个产品类的实例(其中应当注意:这些产品类继承自同一个父类或接口)。
该模式中包含的三个重要角色及其职责如下:
(1)工厂类Factory:工厂类是用来制造产品的。因此,在工厂类中有一个用于制造产品的虚函数。这个函数能够根据参数的不同生成不同的具体产品,这些具体产品都是继承自抽象产品类的。
(2)抽象产品类AbstractProduct:抽象产品是从很多相似的具体产品抽象出来的,作为具体产品类的基类
(3)具体产品类ConcreteProduct:具体产品类继承自抽象产品类,通常有多个。当需要增加新的产品的时候只需增加一个继承自抽象产品类的具体产品类即可。
UML图(没学过这个,有错误的话,见谅)
是不是很难懂?举个简单的例子。
================================================分割线===========================================
比如一个代工厂要生产一批不同版本的华为荣耀7手机,代工厂可以根据手机的不同版本(也就是参数的不同)生产出具体的产品,如移动版荣耀7、联通版手荣耀7、电信版荣耀7。
这里代工厂就是工厂类Factory,华为荣耀7手机就是抽象产品类AbstractProduct,移动版荣耀7、联通版手荣耀7、电信版荣耀7是具体产品类ConcreteProduct。
下面用代码来实现这个简单的例子。
==========================================分割线==================================
/*
*简单工厂模式的一个简单的例子(生产一款手机)
*编译环境:Visual Studio 2013 / win7
*作者:RMYHYMR
*时间:2018/6/29
*/
//文件名main.cpp
#include
using namespace std;
//抽象产品类Honour
class Honour
{
public:
virtual void produce()
{ //仅仅输出一行提示信息
cout << "Produced Honour7..." << endl;
}
};
//具体产品类,移动版荣耀7
class ChinaMobileHonour :public Honour
{
public:
virtual void produce()
{ //重写虚函数
cout << "Produced China Mobile Honour7..." << endl;
}
};
//具体产品类,联通版荣耀7
class ChinaUnicomHonour :public Honour
{
public:
virtual void produce()
{ //重写虚函数
cout << "Produced China Unicom Honour7..." << endl;
}
};
//具体产品类,电信版荣耀7
class ChinaTelecomHonour :public Honour
{
public:
virtual void produce()
{ //重写虚函数
cout << "Produced China Telecom Honour7..." << endl;
}
};
//工厂类Factory
class Factory
{
public://此函数返回一个指向子类对象的基类的指针,通过基类指针访问子类
//static修饰可以在函数调用时通过类名而不是对象
static Honour* createProduced(const char type)
{ //定义一个指向基类对象的指针
Honour * honour_pointer = nullptr;
switch (type)
{
case 'M': //创建一个ChinaMobileHonour对象并赋值给基类指针
honour_pointer = new ChinaMobileHonour();
break;
case 'U': //
honour_pointer = new ChinaUnicomHonour();
break;
case 'T': //
honour_pointer = new ChinaMobileHonour();
break;
default:
break;
}
return honour_pointer;
}
};
int main()
{
char type = 'M';
//创建具体的产品,type作为区分标志。
Honour * honour_pointer = Factory::createProduced(type);
//通过基类指针访问子类的成员函数(虚函数,多态)
honour_pointer->produce();
cout << endl;
return 0;
}
调试结果如下:
对上面的代码我们来简单分析一下
==========================================分割线==================================
首先我们创建了一个抽象产品基类Honour类,有一个成员函数为虚函数,主要输出一行提示信息。然后定义了三个具体产品类公有继承自Honour,并对基类的虚函数进行了重写,输出的提示信息与具体产品类本身相关。最后定义了一个工厂类Factory,其中有一个静态成员函数来实现具体产品类对象的创建工作。最后在主函数中通过使用基类指针来实现对具体产品类的对象的使用。
简单来说,简单工厂模式,有这样的体系结构,所有产品有一个抽象的基类,在基类中有一个或多个虚函数,这个虚函数可以在具体产品类中进行重写(实现一名多用)。对于这些产品的实例化是通过一个工厂类来实现,而不是通过主函数(这样可以使得在以后添加功能,只要添加一个新产品,并在工厂中实例化,而不用修改主函数)。在工厂类中的一个静态成员函数通过传入参数的不同完成对象实例化,并用基类的指针返回,这样在主函数就可以通过基类指针愉快的调用子类重写过的虚函数,实现多态性。
顺便说一下,这种思想(例子可能不是太严谨)其实很常见,学过Qt(或者一些图形界面程序)的都知道,有一个名词叫做子类化窗口部件。在这里也很类似的,基类的功能单一抽象,多个子类可以对基类进行进一步扩展,但在使用子类对象时我们可以使用基类的指针而不是子类的指针,从而使庞杂的子类使用实现用同一种方式调用。
下面看看在书中代码的C++实现
==========================================分割线==================================
/*简单工厂模式的C++代码实现
*编辑器:Visual Studio 2013
*代码来源:根据《大化设计模式》一书改写
*作者:RMYHYMR
*时间:2018/6/29
*QQ:1196727000
*注:此程序为了运行方便,有以下几点:
*1、没有把类的定义和类的实现分不同文件写;
*2、直接使用了类的实现在前,主程序实现在后;
*3、使用了using namespace std;
*4、异常处理简单化;
*5、注释庞杂冗余,可能还会有错误(为了阅读);
*6、程序功能不全,功能单一;
*7、写的不好,欢迎指正
*/
#include
using namespace std;
//此类为实现两个操作数计算的基类(即简单工厂模式中产品的抽象基类)
//此类包含两个操作数和一个抽象计算函数
class Operation
{
public:
//默认构造函数,两个操作数设置为0
Operation() :left_operand(0.0), right_operand(0.0) {};
//初始化构造函数,初始化两个操作数
Operation(double leftNum, double rightNum) :left_operand(leftNum), right_operand(rightNum) {}
//默认虚析构函数(为了父类指针可以调用子类的析构函数,即多态)
virtual ~Operation() {};
//此函数为计算类的抽象方法函数,为具体计算类继承后重写方法(即抽象的产品)
//此函数计算两个数的结果,并将结果返回
virtual double getResult()
{ //result作为两个数的计算结果,基类抽象函数默认返回0
double result = 0.0;
return result;
}
//设置左操作数
void setLeftOperand(double leftNum){ left_operand = leftNum; }
//设置右操作数
void setRightOperand(double rightNum){ right_operand = rightNum; }
//设置两个操作数
void setOperands(double leftNum, double rightNum)
{
left_operand = leftNum;
right_operand = rightNum;
}
protected:
//抽象基类的两个数据成员,作为两个操作数
double left_operand; //左操作数
double right_operand;//右操作数
private:
//无成员
};
//加法类,实现两个数相加,继承自抽象基类(抽象产品的实例化)
class AddOpeatinon :public Operation
{
public:
//默认构造函数,将从基类继承的两个操作数设置为0
AddOpeatinon() :Operation(0.0, 0.0) {}
//初始化构造函数,初始化从基类继承的两个操作数
AddOpeatinon(double leftNum, double rightNum) :Operation(leftNum, rightNum) {}
// 默认虚析构函数(为了父类指针可以调用子类的析构函数,即多态)
virtual ~AddOpeatinon(){}
//重写基类计算函数,此函数计算两个数的结果,并将结果返回
virtual double getResult()
{ //此函数返回两个操作数的和
return left_operand + right_operand;
}
protected:
//无成员
private:
//无成员
};
//减法类, 实现原理见加法类
class SubOpeatinon:public Operation
{
public:
SubOpeatinon() :Operation(0.0, 0.0) {}
SubOpeatinon(double leftNum, double rightNum) :Operation(leftNum, rightNum) {}
virtual ~SubOpeatinon() {}
virtual double getResult()
{
return left_operand - right_operand;
}
protected:
//无成员
private:
//无成员
};
//乘法类, 实现原理见加法类
class MulOpeatinon :public Operation
{
public:
MulOpeatinon() :Operation(0.0, 0.0) {}
MulOpeatinon(double leftNum, double rightNum) :Operation(leftNum, rightNum) {}
virtual ~MulOpeatinon(){}
virtual double getResult()
{
return left_operand * right_operand;
}
protected:
//无成员
private:
//无成员
};
class DivOpeatinon :public Operation
{
public:
DivOpeatinon() :Operation(0.0, 0.0) {}
DivOpeatinon(double leftNum, double rightNum) :Operation(leftNum, rightNum) {}
virtual ~DivOpeatinon(){}
virtual double getResult()
{ //此处如果右操作数为0,则抛出一个double型的异常
if (right_operand == 0)
throw right_operand;
return left_operand / right_operand;
}
protected:
//无成员
private:
//无成员
};
//此类为工厂类,可以认为是软件更新对外的接口类
//如果想添加新的两个操作数的运算可以定义新的计算类,并在运算工厂类中添加实例化方法
class OpeatinonFactory
{
public:
OpeatinonFactory() = default;
~OpeatinonFactory() = default;
//此处定义为static类型是为了后面可以直接使用类名来调用此函数实现计算类的实例化
//返回基类的指针,可以使用基类的指针来访问子类的计算函数(虚函数用法)
//此函数前两个参数为所有计算类都有的两个操作数,后一个参数为不同计算类的区分标志(此处用运算符作为区分标志)
static Operation* createOperation(const double leftNum, const double rightNum, const char binary_operator)
{
//定义一个指向基类的指针,初始化指向子类的对象,调用子类的虚函数(计算函数)
Operation *operation_base_pointer;
//binary_operator双目运算符作为生成具体对象的标志
switch (binary_operator)
{
//加法
case '+':
//基类的指针指向子类加法类的对象。
operation_base_pointer = new AddOpeatinon(leftNum, rightNum);
break;
//减法
case '-':
operation_base_pointer = new SubOpeatinon(leftNum, rightNum);
break;
//乘法
case '*':
operation_base_pointer = new MulOpeatinon(leftNum, rightNum);
break;
//除法
case '/':
operation_base_pointer = new DivOpeatinon(leftNum, rightNum);
break;
}
//返回赋值后的基类的指针,指向的为具体的子类对象
return operation_base_pointer;
}
//此处简单重载了只有一个运算符参数的函数
static Operation* createOperation(const char binary_operator)
{
//定义一个指向基类的指针,初始化指向子类的对象,调用子类的虚函数(计算函数)
Operation *operation_base_pointer;
//binary_operator双目运算符作为生成具体对象的标志
switch (binary_operator)
{
//加法
case '+':
//基类的指针指向子类加法类的对象。
operation_base_pointer = new AddOpeatinon();
break;
//减法
case '-':
operation_base_pointer = new SubOpeatinon();
break;
//乘法
case '*':
operation_base_pointer = new MulOpeatinon();
break;
//除法
case '/':
operation_base_pointer = new DivOpeatinon();
break;
}
//返回赋值后的基类的指针,指向的为具体的子类对象
return operation_base_pointer;
}
protected:
//无成员
private:
//无成员
};
// 客户端程序(以后不用在修改了,增加功能只要新建一个计算类并修改OpeatinonFactory类就行)
int main()
{
try{
//定义两个操作数leftNum、rightNum并初始化为0
double leftNum = 0.0, rightNum = 0.0;
//定义一个运算符变量
char binary_operator;
//定义一个运算结果result并初始化为0
double result = 0.0;
//程序输入两个操作数的值和要进行的运算
cout << "Please enter a floating-point left operand: ";
cin >> leftNum;
cout << "Please enter a floating-point right operand: ";
cin >> rightNum;
cout << "Please select the operation to be performed: ";
cin >> binary_operator;
//调用工厂类的对象实例化函数来初始化基类的指针
//createOperation函数定义为static的好处可以直接用OpeatinonFactory::来调用,而不用定义对象。
Operation *operation_base_pointer = OpeatinonFactory::createOperation(leftNum, rightNum, binary_operator);
//通过基类的指针来调用子类的运算函数
result = operation_base_pointer->getResult();
//输出运算结果
cout << leftNum << " " << binary_operator << " " << rightNum << " = " << result << endl;
//
Operation *operation_base_pointer1 = OpeatinonFactory::createOperation(binary_operator);
operation_base_pointer1->setLeftOperand(leftNum+1);
operation_base_pointer1->setRightOperand(rightNum+1);
result = operation_base_pointer1->getResult();
cout << leftNum + 1 << " " << binary_operator << " " << rightNum + 1 << " = " << result << endl;
}
catch (...){
cerr << "Error!!!" << endl;
exit(1);
}
return 0;
}
具体实现方式和手机类是一样的,只是这里繁琐一点。基类的虚函数,工厂类的对象实例化的静态函数,以及主函数基类指针访问子类,这三点是简单工厂模式比较重要的地方。
==========================================分割线====================================
最后还是来说说优缺点和使用场景。
==========================================分割线====================================
优点
工厂类是简单工厂式的关键,工厂类包含了必要的逻辑判断,根据外界给定的信息决定究竟应该创建哪个具体类的对象。通过使用工厂类,外界可以从直接创建具体产品对象的尴尬局面摆脱出来,仅仅需要负责使用对象就可以了。而不必管这些对象究竟如何创建及如何组织的,明确了各自的职责和权利,有利于整个软件体系结构的优化。
在简单工厂模式中,客户端不再负责对象的创建,而是把这个责任丢给了工厂类,客户端值负责对象的调用,从而明确了各个类的职责(单一职责)。
==========================================分割线====================================
缺点
由于工厂类集中了所有实例的创建逻辑,违反了高内聚责任分配原则。工厂类所能创建的类只能是事先考虑到的,如果需要添加新的类,则就需要改变工厂类了,去修改工厂的代码,这样就违反了开闭原则。同时系统中的具体产品类不断增多时候,可能会出现要求工厂类根据不同条件创建不同实例的需求。这种对条件的判断和对具体产品类型的判断交错在一起,很难避免模块功能的蔓延,对系统的维护和扩展非常不利。
==========================================分割线====================================
使用场景
工厂类负责创建的对象比较少;客户只知道传入工厂类的参数,对于如何创建对象(逻辑)不关心;由于简单工厂很容易违反高内聚责任分配原则,因此一般只在很简单的情况下应用。
==========================================分割线====================================
==========================================分割线====================================
(二)、《大话设计模式》读书笔记 - 策略模式(C++实现)