c++利用模板实现抽象工程模式
抽象工厂模式代码部分
C++越学越觉得神器,短短50行不到代码,不仅克服了单纯传统抽象工厂模式的缺点,用起来还更方便。
不过相对传统的抽象工厂模式,这次用模板实现的抽象工厂抽象度更高,也更不好看懂。
先让我们把一些细节部分去掉,如权限控制,指针管理这些,先只关注这个模板抽象工厂的思路。如果不想看代码可以直接跳到第二部分。
精简源码:
//Factory.h
#pragma once
#include<string>
#include<map>
template<class AbstractProduct>
class AbstractRegister {
public:
AbstractRegister() = default;
virtual AbstractProduct* createProduct() = 0;
};
template<class AbstractProduct>
class Factory {
public:
static AbstractProduct* createProduct(std::string name) {
AbstractRegister<AbstractProduct>* factoryRegister = Factory<AbstractProduct>::name2Product[name];
return factoryRegister->createProduct();
}
static void _factoryRegister(std::string name, AbstractRegister<AbstractProduct>* reg) {
name2Product[name] = reg;
}
static std::map<std::string, AbstractRegister<AbstractProduct>*> name2Product;
};
template<class AbstractProduct> //类内静态成员定义外部初始化
std::map<std::string, AbstractRegister<AbstractProduct>*>Factory<AbstractProduct>::name2Product;
template<class AbstractProduct,class Product> //奇异递归模板
class FactoryRegister : virtual AbstractRegister<AbstractProduct> {
public:
static void factoryRegister(std::string name) {
Factory<AbstractProduct>::_factoryRegister(name,(AbstractRegister<AbstractProduct>*) new FactoryRegister<AbstractProduct, Product>());
}
virtual AbstractProduct* createProduct() {
return (AbstractProduct*)new Product();
}
};
使用:
// ConsoleApplication1.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include"Factory.h"
#include<string>
using namespace std;
class Food {
public:
Food() = default;
virtual ~Food() = default;
virtual void show() = 0;
};
class Cake : virtual Food {
public:
Cake() {
cout << "Food create" << endl;
}
virtual ~Cake() {
cout << "Food delete" << endl;
}
virtual void show() override {
cout << "Cake" << endl;
}
};
int main() {
FactoryRegister<Food, Cake>::factoryRegister("cake"); //注册Food工厂,并在其中加入Cake类
auto cake = Factory<Food>::createProduct("cake");
cake->show();
}
模板抽象工厂思路
如果你看完上面的源码有点懵的话,那是正常的,我第一次也没有理解。但是可以听我慢慢道来。首先我们要理清一些关系。
- FactoryRegister类是向工厂注册产品的类,它和Factory类是双向调用的。
- Factory类是工厂,模板参数是抽象产品类。
- AbstractRegister 和 FactoryRegister 是一个继承关系,这意味着FactoryRegister 的指针可以转换为AbstractRegister 指针。
- 模板参数中AbstractProduct和Product 必须是继承关系,不然程序无法正常运行。
首先最重要的是Factory类中的map对象,map的声明是map<std::string, AbstractRegister*>,kay是一个sting,value是AbstractRegister类的指针,想想上面的关系3,value是AbstractRegister类的指针,FactoryRegister又是 AbstractRegister的子类,这样只要转换FactoryRegister类的指针就可以将FactoryRegister类存入map中。使用map我们得到就一个FactoryRegister 类。
接着我们看FactoryRegister 类,FactoryRegister 类可以说保存这抽象产品和实际产品的信息,只不过这里是使用模板,FactoryRegister 类有两个函数factoryRegister()和createProduct(),factoryRegister()函数提供给外部使用,是使用者向工厂注册产品的接口。createProduct()是专门提供给Factory类使用的。
FactoryRegister 类的createProduct()静态函数也是比较巧妙的,首先createProduct()是一个虚函数,方便转换为AbstractRegister类的时候可以使用FactoryRegister 自身的createProduct()函数,首先虚函数的返回值声明是相同的,所以返回值是 AbstractProduct*,但是我们需要的应该是一个Product啊,没关系,这就是为什么要强调AbstractProduct和Product 必须是继承关系,我们得到AbstractProduct进行一次向下转换即可得到Product*。
最后我们来看下main()函数中的使用。首先为了测试我定义Food类和Cake类,Food类是一个抽象产品,Cake类是一个实际产品。
首先我们使用FactoryRegister<Food, Cake>::factoryRegister(”cake“)静态方法,这个方法将调用Factory类中的_factoryRegister静态方法,这里设计到一个巧妙的转换,还记得上面的关系3吗,子类指针转换为父类指针,将”cake“和FactoryRegister<Food, Cake>类的指针,注册到Factory类的map中。
当要从工厂生产产品,则调用Factory::createProduct(name),这个函数会根据name去查找map中的FactoryRegister类,在使用FactoryRegister类的createProduct()函数创造具体的对象,最后返回。
改良版
打算加上函数访问权限,将裸指针改为智能指针。未完。。。。。
原型模式
说实话第一次看到原型模式,第一反应是构造函数不就能搞定了,但是看到陈硕大佬的一句话,发现网上其实都是废话。
Prototype 的意义在于,你拿到一个 Base* ,它指向某个 Derived 对象,你想克隆出 Derived 对象,但代码中不写出 Derived 的具体类型,因为有很多派生类,这种情况下你用构造函数是搞不定的,type-switch 是 bad smells 。另外,这里考虑 virtual 的性能损失是主次不分,构造对象需要分配内存,这开销比一次虚函数调用大多了。