设计模式:工厂模式

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!

 

自此就实现了反射,即利用类名直接创建对象。

实际开发中可以动态地根据配置创建对象。

 

posted @ 2021-05-07 10:33  Dylan_Liang  阅读(91)  评论(0编辑  收藏  举报