设计模式的一条重要原则是:在系统中引入新功能时,千万别去修改老代码,只去添加新代码即可!
==========================================================================
一、工厂模式概述:工厂工厂,顾名思义就是负责加工生产的方法,工厂模式属于“创建型设计模式”,他提供了一种创建对象的方式,不会对客户端暴露创建对象的逻辑,并且使用抽象工厂提供的公共接口来完成对象的创建,一句话概况就是工厂模式封装了不同对象的创建过程。
二、应用场景:
如果我们只有一个类classA,那需要用到他的时候直接new classA()即可,这最简单的。但是如果存在多个类classA,classB,classC,而且这些类长得比较相似,但在使用的时候需要根据不同的情况来创建不同的对象。此时就需要用到工厂模式了,实现客户端与目标类之间的解耦,客户端根本就不需要知道有哪些类,也不需要知道如何去实例化,它只需要提出条件,然后工厂就基于客户端提出的条件来决定实例化哪一个类。
三、场景模拟:利用工厂模式实现“加减乘除四则运算”。
工厂模式UML类图:
**废话少说,直接挒上实现代码
步骤1:定义抽象运算类Operation
class Operation
{
public:
Operation() {}
virtual ~Operation() {}
virtual double GetResult() = 0;
void setA(double a) {
this->mNumberA = a;
}
void setB(double b) {
this->mNumberB = b;
}
protected:
double mNumberA;
double mNumberB;
};
步骤2:从抽象运算类中派生出具体运算类
// 加法运算类
class OperationAdd :public Operation
{
public:
OperationAdd() {}
~OperationAdd() {}
virtual double GetResult()
{
return (mNumberA + mNumberB);
}
};
// 减法运算类
class OperationSub :public Operation
{
public:
OperationSub() {}
~OperationSub() {}
virtual double GetResult()
{
return (mNumberA - mNumberB);
}
};
// 乘法运算类
class OperationMul :public Operation
{
public:
OperationMul() {}
~OperationMul() {}
virtual double GetResult()
{
return (mNumberA * mNumberB);
}
};
// 除法运算类
class OperationDiv :public Operation
{
public:
OperationDiv() {}
~OperationDiv() {}
virtual double GetResult()
{
return (mNumberA / mNumberB);
}
};
步骤3:定义抽象工厂类
class AbstractFactory
{
public:
AbstractFactory() {}
virtual ~AbstractFactory() {}
virtual Operation* CreateOperation() = 0; // 声明一个专门用来创建对象的公共接口,具体工厂类继承并重写
};
步骤4:定义具体的工厂类
// 加法工厂(只负责生产加法对象)
class FactoryAdd :public AbstractFactory
{
public:
FactoryAdd() {}
~FactoryAdd() {}
// 重写虚函数CreateOperation(),在其内部创建加法类对象并返回
virtual Operation* CreateOperation()
{
return (new OperationAdd());
}
};
// 减法工厂(只负责生产减法对象)
class FactorySub :public AbstractFactory
{
public:
FactorySub() {}
~FactorySub() {}
virtual Operation* CreateOperation()
{
return (new OperationSub());
}
};
// 乘法工厂(只负责生产乘法对象)
class FactoryMul :public AbstractFactory
{
public:
FactoryMul() {}
~FactoryMul() {}
virtual Operation* CreateOperation()
{
return (new OperationMul());
}
};
// 除法工厂(只负责生产除法对象)
class FactoryDiv :public AbstractFactory
{
public:
FactoryDiv() {}
~FactoryDiv() {}
virtual Operation* CreateOperation()
{
return (new OperationDiv());
}
};
客户端代码:
#include <QtCore/QCoreApplication>
#include <iostream>
#include "Factory.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
char sign = '/';
double result = 0;
double A = 2.5, B = 0.5;
// 把逻辑判断部分转移到客户端中去
// 根据不同的条件,在各条case分支语句中 new 出不同的工厂对象,从而间接实例化不同的运算类
AbstractFactory* operFactory = nullptr;
Operation* oper = nullptr;
switch (sign)
{
case '+':
operFactory = new FactoryAdd();
oper = operFactory->CreateOperation();
oper->setA(A);
oper->setB(B);
result = oper->GetResult();
break;
case '-':
operFactory = new FactorySub();
oper = operFactory->CreateOperation();
oper->setA(A);
oper->setB(B);
result = oper->GetResult();
break;
case '*':
operFactory = new FactoryMul();
oper = operFactory->CreateOperation();
oper->setA(A);
oper->setB(B);
result = oper->GetResult();
break;
case '/':
operFactory = new FactoryDiv();
oper = operFactory->CreateOperation();
oper->setA(A);
oper->setB(B);
result = oper->GetResult();
break;
}
std::cout << A << sign << B << "=" << result;
delete operFactory;
delete oper;
return a.exec();
}
在客户端中主要根据运算符sign的不同来动态选择具体的运算方法,当选择了除法运算时,结果如下:
四、总结
1、工厂模式的优点:
(1)扩展性好,如果想要增加一个产品,只需要扩展工厂类即可。
(2)封装性好,在客户端内想要创建一个对象时,并不需要知道该对象的创建细节,至需要知道其对应的工厂名即可。
(3)耦合性低,将具体产品的实现与客户端分离,使用者只需要关心产品的接口。
2、简单工厂模式VS工厂模式:
相同点:两种方法在处理产品类部分的思路是一致的,都是声明一个抽象产品类,然后从抽象产品类派生出各个具体的产品类,主要根据C++虚函数和多态的原理来动态地选择实例化哪个产品类。
不同点:两种方法在处理工厂类部分的思路不同。简单工厂模式直接就只有一个工厂类,在实例化产品类的时候,是把大量逻辑判断(switch、if)都放在了唯一的工厂类中,然后根据客户端设置的条件来动态选择实例化具体的产品类。然而工厂模式在此基础上做了升级,首先定义了一个抽象工厂,从抽象工厂中派生出具体工厂类,这样做的意义在于把原来简单工厂中的逻辑判断转移到了客户端中去。
当后期需要引入新的产品时,简单工厂方法需要修改工厂类中的逻辑判断和增加产品类,既做了修改也做了扩展;而工厂方法只需要增加新的具体工厂类和具体产品类即可,仅仅是做了扩展。后者符合“开闭原则”。
3、个人的顶级理解:
各种工厂模式,无论是简单工厂还是普通工厂还是抽象工厂,说白了,其实都在解决对象如何更加合理地进行创建的问题(new运算)。利用抽象类在顶层设计好统一的接口,把实例化过程延迟到子类中去实现,并做集中管理,这样就把实例化的过程从业务逻辑中解耦出去了。