摘要:
it人员无论是使用哪种高级语言开发东东,想要更高效有层次的开发程序的话都躲不开三件套:数据结构,算法和设计模式。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案,使用设计模式是为了重用代码、让代码更容易被他人理解、保证代码可靠性。
设计模式坚持七大原则:开闭原则,单一职责原则,里氏替换原则,依赖倒转原则,接口隔离原则,迪米特原则,合成复用原则,而各项设计模式又区分为三大模式,创建型模式,结构型模式,行为模式。
此系列专注讲解C++开发过程中高需求好用的设计模式类型,能更好的简练项目架构和代码量,通过使用场景以及代码实现来介绍对应的模式。本文介绍的是工厂模式factory。
(开发环境:VSCode,GCC13.2.0,cmake3.8)
关键词
: C++,设计模式,工厂模式,Design pattern,factory
声明:
本文作者原创,转载请附上文章出处与本文链接。
文章目录
- 摘要:
- 正文:
- 介绍
- 使用场景
- 注意事项
- 代码实现
- 简易工厂模式
- 抽象工厂模式(工厂方法模式的拓展)
- 用类模板或者宏定义实现通用工厂模式
- 推荐阅读
正文:
介绍
工厂模式(Factory Pattern)是一种常用的创建型设计模式,它提供了一种创建对象的最佳方式。工厂模式的核心在于将对象的实例化过程封装在一个或多个专门的工厂类中,从而降低了客户端代码与具体产品类之间的耦合度,使得系统更加灵活和易于扩展。工厂模式主要包括三种类型:简单工厂模式(Simple Factory Pattern)、工厂方法模式(Factory Method Pattern)和抽象工厂模式(Abstract Factory Pattern),以及最后用类模板搭建的通用抽象工厂。
其模式实质是对C++虚函数,继承,类模板等特性的抽象应用,所以需要对这几个特性有一定理解才能更好的搭建工厂。
- 简单工厂模式:
- 又称静态工厂模式,通过一个专门的工厂类来创建对象,无需将对象的实例化过程放在客户端代码中。
- 工厂类根据客户端的请求,返回相应的产品实例。
- 优点:简单易用,减少了客户端代码与具体产品类的直接耦合。
- 缺点:当产品种类增多时,工厂类的代码会变得复杂,违反了开闭原则(对扩展开放,对修改关闭)。
- 工厂方法模式:
- 定义一个用于创建对象的接口,但让子类决定实例化哪个类。
- 工厂方法让类的实例化推迟到子类中进行。
- 优点:提高了系统的可扩展性和灵活性,符合开闭原则。
- 缺点:当产品种类非常多时,会增加系统的复杂性。
- 抽象工厂模式(工厂方法模式的拓展):
- 提供了一个创建一系列相关或相互依赖对象的接口,而无需指定具体类。
- 客户端可以通过抽象工厂来创建多个系列的产品族。
- 优点:能够创建一系列相互关联或相互依赖的对象,并且增加了新的产品族时无需修改现有代码。
- 缺点:系统结构的复杂性较高,增加了系统的抽象性和理解难度。
使用场景
作为一种创建类模式,在需要生成复杂对象的地方,都可以使用工厂模式,例如可以创建页面工厂,而简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用设计模式。
- 简单工厂模式:适用于产品种类较少且不经常变化的场景。
- 工厂方法模式:适用于产品种类较多,且每种产品都有对应的创建逻辑的场景。
- 抽象工厂模式:适用于需要创建一系列相互关联或相互依赖的对象的场景,如产品族。
简单工厂模式和抽象工厂模式并没有上下级之分,具体运用就看项目需求,通常简单工厂模式用的会更多点。
注意事项
系统复杂性增加,特别是抽象工厂模式,当产品族较多时,系统结构会变得复杂,并增加代码量,需要为每种产品或产品族创建相应的类和接口。
代码实现
简易工厂模式
简单工厂模式就是用于获取建立在堆上的对象,根据具体的类编写具体的工厂生产方式,这里举个例子:你要去买一台手机,你不用关心手机是怎么生产出来的,里面的零件具体又是怎么制造的,这些通通都交给工厂去处理,你尽管去买手机就好了。
#pragma once
#include <iostream>
using namespace std;
// 手机基类
class Phone {
public:
virtual void boot_up() = 0;
virtual ~Phone() {}
public:
int id;
};
class XiaoMi_Phone :public Phone {
public:
XiaoMi_Phone() {}
XiaoMi_Phone(int _id) {} // 无需但必要的
XiaoMi_Phone(int _id, string _str):str(_str)
{
id = _id;
}
virtual void boot_up() {
cout << "XIAOMI:" << id << endl;
}
protected:
string str;
};
class OnePuls_Phone :public Phone {
public:
OnePuls_Phone() {}
OnePuls_Phone(int _id)
{
id = _id;
}
OnePuls_Phone(int _id, string _str) {} // 无需但必要的
virtual void boot_up() {
cout << "ONEPULS:" << id << endl;
}
};
enum
{
XiaoMi,
OnePuls
};
// 简易工厂类
class Factory {
public:
template<typename ... Args>
Phone *pruduce(int val, Args ... args);
};
template<typename ... Args>
inline Phone *Factory::pruduce(int val, Args ... args)
{
if (val == XiaoMi) {
return new XiaoMi_Phone(std::forward<Args>(args)...);
}
else if (val == OnePuls) {
return new OnePuls_Phone(std::forward<Args>(args)...);
}
else
return nullptr;
}
int main()
{
Factory factory;
XiaoMi_Phone *p1 = (XiaoMi_Phone *)factory.pruduce(XiaoMi, 1, "卢总");
OnePuls_Phone *p2 = (OnePuls_Phone *)factory.pruduce(OnePuls, 2);
OnePuls_Phone *p3 = (OnePuls_Phone *)factory.pruduce(OnePuls, 3);
p1->boot_up();
p2->boot_up();
p3->boot_up();
return 0;
}
简单工厂模式可以完成对对象的生成,但是可扩展性和维护性并不强,每多出一个品牌例如华为手机便需要修改已有的工厂pruduce
生产接口,也没有产品族设计,小米也不只有手机,一加也还有做平板,如果后续需要拓展维护就会变得困难,工厂方法模式就是进一步优化这些问题。
用了个可变参数模板
template<typename ... Args>
,应付于类不同的构造函数参数,不过此方式有一定的不足,不足在于Phone的派生类需要有相同的构造函数参数集,为了Factory::pruduce
能正常编译,这个涉及到if或者说条件分支的条件检查是一个运行时行为。即使编译期就知道条件一定是个false,then也必须编,或许可以用constexpr解决,博文后面也有更完善的做法,此代码主要用来理解概念。
抽象工厂模式(工厂方法模式的拓展)
抽象工厂模式是工厂方法模式的拓展,工厂方法模式上做出的改动不大,实现实质以及拥有的功能基本一致的,抽象工厂模式比工厂方法模式多解决了产品族的问题,也即是搭建“品牌”工厂。现在在原有的工厂基础上需要添加华为品牌以及能够生产华为手环和小米手环,并且为了后续可能存在的拓展维护,我们就需要设计抽象工厂模式。
#pragma once
#include <iostream>
#include <memory>
using namespace std;
// 手机基类
class Phone {
public:
virtual void boot_up() = 0;
virtual ~Phone() {}
int id;
};
class XiaoMi_Phone :public Phone {
public:
XiaoMi_Phone() {}
XiaoMi_Phone(int _id, string _str):str(_str)
{
id = _id;
}
virtual void boot_up() {
cout << "XIAOMI_Phone:" << id << endl;
}
protected:
string str;
};
class OnePuls_Phone :public Phone {
public:
OnePuls_Phone() {}
OnePuls_Phone(int _id)
{
id = _id;
}
virtual void boot_up() {
cout << "ONEPULS_Phone:" << id << endl;
}
};
class HuaWei_Phone :public Phone {
public:
HuaWei_Phone() {}
HuaWei_Phone(int _id)
{
id = _id;
}
virtual void boot_up() {
cout << "HUAWEI_Phone:" << id << endl;
}
};
// 手表基类
class Watch {
public:
virtual void start() = 0;
virtual ~Watch() {}
string brand;
};
class XiaoMi_Watch :public Watch {
public:
XiaoMi_Watch() {}
XiaoMi_Watch(string _str)
{
brand = _str;
}
virtual void start() {
cout << "XIAOMI_WATCH:" << brand << endl;
}
};
class HuaWei_Watch :public Watch {
public:
HuaWei_Watch() {}
HuaWei_Watch(string _str, int _price):price(_price)
{
brand = _str;
}
virtual void start() {
cout << "HUAWEI_WATCH:" << brand << price << endl;
}
protected:
int price;
};
// 抽象工厂基类
class Factory {
public:
virtual Phone *pruduce_phone() = 0;
virtual Watch *pruduce_watch() = 0; // 可拓展产品族,无需改动Phone相关代码
virtual ~Factory() {}
};
// 品牌工厂
class XiaoMi_Factory : public Factory {
public:
virtual XiaoMi_Phone *pruduce_phone() { return new XiaoMi_Phone; }
virtual XiaoMi_Watch *pruduce_watch() { return new XiaoMi_Watch; }
};
class OnePuls_Factory : public Factory {
public:
virtual OnePuls_Phone *pruduce_phone() { return new OnePuls_Phone; }
virtual Watch *pruduce_watch() { return nullptr; }
};
class HuaWei_Factory : public Factory { // 添加品牌而不需要改Factory
public:
virtual HuaWei_Phone *pruduce_phone() { return new HuaWei_Phone; }
virtual HuaWei_Watch *pruduce_watch() { return new HuaWei_Watch; }
};
int main()
{
shared_ptr<Factory> f1(new XiaoMi_Factory());
shared_ptr<Factory> f2(new OnePuls_Factory());
shared_ptr<Factory> f3(new HuaWei_Factory());
XiaoMi_Phone *p1 = (XiaoMi_Phone *)f1->pruduce_phone();
OnePuls_Phone *p2 = (OnePuls_Phone *)f2->pruduce_phone();
HuaWei_Phone *p3 = (HuaWei_Phone *)f3->pruduce_phone();
p1->boot_up();
p2->boot_up();
p3->boot_up();
XiaoMi_Watch *w1 = (XiaoMi_Watch *)f1->pruduce_watch();
HuaWei_Watch *w3 = (HuaWei_Watch *)f3->pruduce_watch();
w1->start();
w3->start();
return 0;
}
这样就存在了品牌工厂,并且各个工厂只能生产对应的品牌,方便后续拓展和维护,不过代码还存在一定问题,如果各个产品有不同的数据,则不方便在构造时进行初始化数据,主要原因是虚函数继承问题,子类想要重写父类的virtual
函数,那么子类的这个函数的返回类型,名字,参数列表全部都得和父类一样,有两种解决方式,一种设置InitData
成员函数,实例成功后调用设置初始值;另一种为统一同一类型产品的构造函数,Factory
虚函数参数好进行统一,或者接着往下看通用工厂模式~。
用类模板或者宏定义实现通用工厂模式
虽然抽象工厂模式维护相对比较方便,不过每次有新的类出现的时候,我们还是需要去增加接口以便能够产生新的产品,所以为了能更方便的工作(摸鱼),我们可以用类模板或者宏定义实现通用的工厂模式,以下两种方式都是一个工厂生产所有。
用类模板和可变参数模板实现:
#pragma once
#include <iostream>
using namespace std;
// 手机基类
class Phone {
public:
virtual void boot_up() = 0;
virtual ~Phone() {}
public:
int id;
};
class XiaoMi_Phone :public Phone {
public:
XiaoMi_Phone(int _id, string _str):str(_str)
{
id = _id;
}
virtual void boot_up() {
cout << "XIAOMI:" << id << str << endl;
}
protected:
string str;
};
class OnePuls_Phone :public Phone {
public:
OnePuls_Phone(int _id)
{
id = _id;
}
virtual void boot_up() {
cout << "ONEPULS:" << id << endl;
}
};
// 手表基类
class Watch {
public:
virtual void start() = 0;
virtual ~Watch() {}
string brand;
};
class XiaoMi_Watch :public Watch {
public:
XiaoMi_Watch(string _str)
{
brand = _str;
}
virtual void start() {
cout << "XIAOMI_WATCH:" << brand << endl;
}
};
class HuaWei_Watch :public Watch {
public:
HuaWei_Watch(string _str, int _price):price(_price)
{
brand = _str;
}
virtual void start() {
cout << "HUAWEI_WATCH:" << brand << price << endl;
}
protected:
int price;
};
class Factory {
public:
template<typename T, typename ... Args>
T *pruduce(Args ... args);
};
template<typename T, typename ... Args>
inline T *Factory::pruduce(Args ... args)
{
return new T(std::forward<Args>(args)...);
}
int main()
{
Factory factory;
XiaoMi_Phone *p1 = factory.pruduce<XiaoMi_Phone>(1, "王腾大帝");
OnePuls_Phone *p2 = factory.pruduce<OnePuls_Phone>(2);
XiaoMi_Watch *w1 = factory.pruduce<XiaoMi_Watch>("areyouok");
HuaWei_Watch *w2 = factory.pruduce<HuaWei_Watch>("遥遥领先", 666);
p1->boot_up();
p2->boot_up();
w1->start();
w2->start();
return 0;
}
另一种有意思的宏定义写法,用到__VA_ARGS__
,lamble
,对象切割,类型推导等,并且多加了个STL容器保存产出的产品实例,不过由于需要保存产品的话,则多加一种产品需要多加一个STL容器,并重载registerGoods,unregisterGoods
函数。
#pragma once
#include <iostream>
#include <memory>
#include <functional>
#include <unordered_map>
#include <string>
using namespace std;
// 手机基类
class Phone {
public:
virtual void boot_up() = 0;
virtual ~Phone() {}
public:
int id;
};
class XiaoMi_Phone :public Phone {
public:
XiaoMi_Phone(int _id, string _str):str(_str)
{
id = _id;
}
virtual void boot_up() {
cout << "XIAOMI" << endl;
}
protected:
string str;
};
class ONEPULS_Phone :public Phone {
public:
ONEPULS_Phone(int _id)
{
id = _id;
}
virtual void boot_up() {
cout << "ONEPULS" << endl;
}
};
// 注册产品(根据控件类名)
#define REGISTER(factory, T, ctrlClass, ...) \
factory.registerGoods(#ctrlClass, \
[]() -> T { \
T obj(new ctrlClass(__VA_ARGS__)); \
return obj; \
});
// 注销产品(根据类名)
#define UNREGISTER(factory, T, ctrlClass) factory.registerGoods(#ctrlClass, T);
class Factory
{
public:
// 防止拷贝
Factory& operator=(const Factory&) = default;
virtual ~Factory() { PgoodsHash.clear(); }
using Pptr = std::function<shared_ptr<Phone> ()>; // shared_ptr<Phone>对象切割,std::function类型推导
// using Wptr = std::function<shared_ptr<Watch> ()>;
void registerGoods(const string key, const Pptr &value); // 注册Phone产品
void unregisterGoods(const string key, const Pptr &value); // 注销Phone产品
unordered_multimap<string, Pptr> *GetHash(){ return &PgoodsHash; }
private:
// 工厂保存出厂产品的实例
unordered_multimap<string, Pptr> PgoodsHash;
};
inline void Factory::registerGoods(const string key, const Pptr &value)
{
PgoodsHash.insert(make_pair(key, value));
}
inline void Factory::unregisterGoods(const string key, const Pptr &value)
{
PgoodsHash.erase(key);
}
int main()
{
Factory factory;
REGISTER(factory, shared_ptr<Phone>, XiaoMi_Phone, 1, "雷神")
REGISTER(factory, shared_ptr<Phone>, ONEPULS_Phone, 1)
REGISTER(factory, shared_ptr<Phone>, ONEPULS_Phone, 2)
auto value = factory.GetHash()->equal_range("XiaoMi_Phone");
value.first->second()->boot_up();
cout << "遍历:" << endl;
for (const auto pair : *factory.GetHash()) {
cout << pair.first << pair.second()->id << endl;
pair.second()->boot_up() ;
}
return 0;
}
over~
推荐阅读
博客主页:https://blog.csdn.net/weixin_45068267
(客官逛一逛,有许多其它有趣的专栏博文)
C/C++专栏:https://blog.csdn.net/weixin_45068267/category_12268204.html
(内含其它设计模式的介绍和实现)