设计模式:工厂模式
1. 工厂模式是什么:
把创建对象的过程比作一个工厂中生产产品的过程,即在一个工厂中(工厂类)统一创建不同类型的产品(对象)。
利用了C++面向对象中的继承来实现。
2. 工厂模式为了解决什么问题:
1). 不同的类,需要不同的创建过程。如不同种类的动物,根据不同的场景会写下如下条件分支的代码:
if (flag == "dog") { new dog; } else if (flag == "cat") { new cat; }
显然当类足够多时,这种写法是不够优雅的。
需要一种统一且简单的方式来创建。
2). 为了封装创建对象的具体细节及逻辑,在大型复杂的项目下通过一种统一的方式 来创建对象。
3. 实现:
网上对工厂模式做了些分类;
1) 简单工厂:
一个工厂类负责创建,但还是用到了条件判断,不利于扩展。
BaseProduct* Factory::CreateProduct(int nProductType){ switch (nProductType){ case 1: return new Product1; case 2: return new Product2; default: break; } return NULL; }
2) 工厂模式方法:
一个产品对应一个工厂,少了在一个工厂类中进行判断,有利于程序的扩展,当需要新赠一个产品时不需要修改代码,满足程序设计的开闭原则(开放扩展关闭修改)。
class Product{ }; class Product1 : public Product{ }; class Product2 : public Product{ }; class Factory{ public: virtual Product* Create() = 0; }; class Factory1 : public Factory{ public: Product* Create(){ return new Product1; } }; class Factory2 : public Factory{ public: Product* Create(){ return new Product2; } };
3) 抽象工厂模式:
一个工厂可对应多个产品,但不同产品是通过不同接口来实现的。
为了解决一个工厂可以生产多个产品的问题,代码与工厂模式方法类似。
参考:https://blog.csdn.net/wuzhekai1985/article/details/6660462
以上三种方法,在新增产品时,要么新增产品类、要么修改工厂类,感觉并不是很好的设计。
4) 模板工厂:
还是一个工厂对应一个种类的产品,只不过,不需要手动编写不同的工厂类,工厂类是通过模板类来实现。减少了代码量
相当于给工厂类传递了一个参数,这个参数是在创建工厂对象时赋值。
class BaseProduct1 { public: BaseProduct1() {} virtual ~BaseProduct1() {} }; template<typename AbstractProduct> class AbstractFactory { public: AbstractFactory() {} virtual ~AbstractFactory() {} virtual AbstractProduct* Create() = 0; }; template<typename AbstractProduct, typename ConcreteProduct> class ConcreteFactory : public AbstractFactory<AbstractProduct> { public: ConcreteFactory() {} virtual ~ConcreteFactory() {} virtual AbstractProduct* Create() { return new ConcreteProduct(); } }; class ProductA : public BaseProduct1 { public: ProductA(){ std::cout << "Create ProductA.\n"; } virtual ~ProductA(); }; class ProductB : public BaseProduct1 { public: ProductB(){ std::cout << "Create ProductB.\n"; } virtual ~ProductB(); }; int main() { // 创建产品A ConcreteFactory<BaseProduct1, ProductA> AFactorty; BaseProduct* pAPro = AFactorty.Create(); // 创建产品B ConcreteFactory<BaseProduct1, ProductB> BFactorty; BaseProduct* pBPro = BFactorty.Create(); }
5) 产品注册类+单例工厂模板类:
基于模板工厂仔细思考下可以优化的点:
1. 一个工厂类就可以生产不同种类的产品,其实这个工厂类可以为一个单例,不需要每次创建。
2. 创建一个产品时还需要通过这个产品类来构建工厂类再进行创建,其实有些没有必要。
因为既然都知道了产品类了,对于简单的创建来讲就可以直接通过这个产品类来创建了,没必要再通过工厂类来绕一圈。
可不可以只知道产品的名称就能创建某个产品呢?这可能才是应用中需要的场景,这个场景可以用简单工厂来实现,但简单工厂又需要修改代码,违背了开闭原则。
接下来引入一个注册类来解决这个问题。
可以通过产品注册的方式,将产品对象储存在单例工厂类的map中。这样就可以通过产品名称来创建产品,又没有违背开闭原则。
而对于注册的时机不同有不同的实现方式,
a. 如果考虑只能在运行过程中注册产品,则需要先创建注册类,再调用工厂对象进程产品的创建。
b. 如果希望编译期就完成产品的注册,则运行过程中只需要关注产品的创建。这种需求就需要利用C++静态、全局变量的特性在程序开启时完成注册。
由于本人在工作中使用的是第二种实现方式,即开发中新增一个产品类时就将其注册,使用时只用关系创建。详见反射加工厂模式
4. 反射加工厂模式
目的:
通过字符串或者key就可以创建出对象,并且新政类不需要改动之前的代码。
实现方式:
1. 需要一个map存储的是:key和对应的创建对象的函数。
2. 利用静态初始化的特性,在程序启动的时候就将key和构建函数存储在map中,key可以通过宏定义由类名构成。
3. 创建对象的函数可以统一由宏定义完成,静态初始化可以利用全局变量的特性,也可以用宏定义完成。// ClassFactory.h
typedef void* (*NewFunc)(); using namespace std; class ClassFactory { public: static ClassFactory* Instance() { static ClassFactory instance; return &instance; } static BaseProduct* NewObject(std::string strName); static bool RegisterClass(string str, NewFunc func); private: ClassFactory() = default; ~ClassFactory() = default; ClassFactory(const ClassFactory& other) = delete; ClassFactory& operator=(const ClassFactory& other) = delete; static map<string, NewFunc> m_mapClassNewFunc; }; #define REGIST_CLASS(class_name)\ void* class_name##NewFunc(){\ return new class_name();\ }\ bool class_name##RegisterFlag = ClassFactory::Instance()->RegisterClass(#class_name, class_name##NewFunc); // ClassFactory.cpp
map<string, NewFunc> ClassFactory::m_mapClassNewFunc; BaseProduct* ClassFactory::NewObject(std::string strName) { auto iter = m_mapClassNewFunc.find(strName); if (iter == m_mapClassNewFunc.end()) { cout << "no class registed!" << endl; return nullptr; } return (BaseProduct*)iter->second(); } bool ClassFactory::RegisterClass(string str, NewFunc func) { m_mapClassNewFunc[str] = func; return true; }
选要被工厂类创建的产品类:
class ProductA : public BaseProduct { public: ProductA() { std::cout << "Create ProductA.\n"; } virtual ~ProductA() {} }; REGIST_CLASS(ProductA)
class ProductB : public BaseProduct { public: ProductB() { std::cout << "Create ProductB.\n"; } virtual ~ProductB() {} }; REGIST_CLASS(ProductB)
调用:
int main() { BaseProduct* pA = ClassFactory::Instance()->NewObject("ProductA"); BaseProduct* pB = ClassFactory::Instance()->NewObject("ProductB"); BaseProduct* pC = ClassFactory::Instance()->NewObject("ProductC"); }
输出:
Create ProductA. Create ProductB. no class registed!
自此就实现了反射,即利用类名直接创建对象。
实际开发中可以动态地根据配置创建对象。