简单工厂模式(Simple Factory)
UML类图示法
矩形框三层:
- 第一层:类的名称,若为抽象类,斜体显示
- 第二层:类的特性,通常是字段和属性
- 第三层:类的操作,通常是方法或行为
- 符号:'+':public, '-':private,'#':protected
类与类,类与接口之间的关系
- 继承:空心三角形+实线
- 接口:空心三角形+虚线
- 关联:实线箭头
- 聚合:空心菱形+实线箭头
- 合成(组合)关系:实心的菱形+实线箭头
简单工厂模式
概念
简单工厂模式(Simple Factory Pattern):又称为静态工厂方法(Static Factory Method)模式,它属于类创建型模式。在简单工厂模式中,可以根据参数的不同返回不同类的实例。简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。
模式结构
主要用于创建对象。新添加类时,不会影响以前的系统代码。核心思想是用一个工厂来根据输入的条件产生不同的类,然后根据不同类的 virtual 函数得到不同的结果。
工厂角色(Creator) | 是简单工厂模式的核心,它负责实现创建所有具体产品类的实例。工厂类可以被外界直接调用,创建所需的产品对象。 |
---|---|
抽象产品角色(Product) | 是所有具体产品角色的父类,它负责描述所有实例所共有的公共接口。 |
具体产品角色(Concrete Product) | 继承自抽象产品角色,一般为多个,是简单工厂模式的创建目标。工厂类返回的都是该角色的某一具体产品。 |
模式实例与解析(1)
考虑一个简单的应用场景,一个计算器可以提供多个不同的运算符(如加法、减法、乘法、除法等),这些运算符都源自同一个基类,不过在继承基类后不同的子类修改了部分属性从而使得它们可以呈现不同的外观,如果我们希望在使用这些运算符时,不需要创建这些子类,只需要知道我要做的是加法还是减法,并把符号传入方法即可返回一个结果,此时,就可以使用简单工厂模式。
注:所有用例只为体现设计模式思想,不考虑边界情况,如不考虑除数为0异常等。
#include <iostream>
using namespace std;
class Operation{
public:
virtual double GetResult() = 0;
double GetNumA() { return _numA; }
double GetNumB() { return _numB; }
void SetNum(double a, double b)
{
_numA = a;
_numB = b;
}
virtual ~Operation() = default;
private:
double _numA = 0;
double _numB = 0;
};
class OperationAdd : public Operation
{
public:
virtual double GetResult()
{
double res = 0;
res = GetNumA() + GetNumB();
return res;
}
};
class OperationSub : public Operation
{
public:
virtual double GetResult()
{
double res = 0;
res = GetNumA() - GetNumB();
return res;
}
};
class OperationMul : public Operation
{
public:
virtual double GetResult()
{
double res = 0;
res = GetNumA() * GetNumB();
return res;
}
};
class OperationDiv : public Operation
{
public:
virtual double GetResult()
{
double res = 0;
res = GetNumA() / GetNumB();
return res;
}
};
class CalculatorFactory
{
public:
static Operation* Create(char c)
{
Operation *oper;
switch(c)
{
case '+':
oper = new OperationAdd();
break;
case '-':
oper = new OperationSub();
break;
case '*':
oper = new OperationMul();
break;
case '/':
oper = new OperationDiv();
break;
default:
oper = new OperationAdd();
break;
}
return oper;
}
};
int main()
{
int a, b;
cin >> a >> b;
Operation * op = CalculatorFactory::Create('+');
op->SetNum(a, b);
double result = op->GetResult();
cout << "result = " << result << endl;
}
从此以后,如果要加入新的运算符,只需要在新增子类并且在工厂类中的添加相应符号。
模式实例与解析(2)
再考虑一个简单的应用场景,某商店定期有促销活动,有满300返100,打八折,打五折,满200返50,等等,这些优惠都源自同一个基类,这也可以用工厂模式。
#include <iostream>
#include <string>
#include <unordered_map>
using namespace std;
unordered_map<string, int> mp{{"正常收费", 0}, {"满300返100", 1}, {"打八折", 2}};
class CashSuper
{
public:
virtual double accpetCash(double money) = 0;
};
class CashNormal : public CashSuper
{
public:
virtual double accpetCash(double money){ return money; }
};
class CashRebate : public CashSuper
{
public:
CashRebate(double moneyRebate):_moneyRebate(moneyRebate){}
virtual double accpetCash(double money) { return money * _moneyRebate; }
private:
double _moneyRebate = 0.9;
};
class CashReturn : public CashSuper
{
public:
CashReturn(double moneyCondition, double moneyReturn)
:_moneyCondition(moneyCondition), _moneyReturn(moneyReturn){}
double accpetCash(double money)
{
double result = money;
if(money >= _moneyCondition)
{
result = money - (money / _moneyCondition) * _moneyReturn;
}
return result;
}
private:
double _moneyCondition = 0.0;
double _moneyReturn = 0.0;
};
class CashFactory
{
public:
static CashSuper* createCashAccept(string type)
{
int StringValue = mp[type];
CashSuper * cs = nullptr;
switch(StringValue)
{
case 0:
cs = new CashNormal();
break;
case 1:
cs = new CashReturn(300, 100);
break;
case 2:
cs = new CashRebate(0.8);
break;
}
return cs;
}
};
int main()
{
double total = 0.0;
string super;
cin >> super;
CashSuper *p = CashFactory::createCashAccept(super);
total = p->accpetCash(300);
cout << "total = " << total << endl;
return 0;
}
因为C++中switch(expression)中的expression不支持string类表达式,所以为实现功能这里额外调用了unordered_map来匹配字符串。
工厂类中的方法必须是static方法,于是可以通过类名访问,也可以通过类的实例访问。而普通方法又叫实例方法,只能通过类的实例访问。当你需要什么,只需要传入一个正确的参数,就可以获取你所要的的对象,而无需知道其创建的细节。如果不用static反而违背了设计模式的初衷!
优缺点
优点:实现对象的创建和对象的使用分离,将对象的创建交给专门的工厂类负责
缺点:工厂类不够灵活,增加新的具体产品需要修改工厂类的判断逻辑代码,而且产品较多时,工厂方法代码将会非常复杂。
参考: