首页 > 编程笔记 > C++笔记 阅读:1

C++抽象工厂模式的具体实现(非常详细)

抽象工厂模式(Abstract Factory Pattern)提供了一种封装一组相关或相互依赖对象的创建方式而无须指定其具体类。

在软件开发中有时需要创建一组相关的对象,这些对象之间可能存在关联或依赖关系。如果直接在客户端代码中创建这组对象,则将导致代码耦合度高、难以维护和扩展。在这种情况下使用抽象工厂模式可以很好地解决这个问题。

抽象工厂模式的传统结构

抽象工厂模式通过引入一个抽象工厂接口定义一组用于创建相关对象的方法。每个具体的工厂类都实现了抽象工厂接口,并负责创建一组相关的对象。客户端代码通过调用工厂接口的方法来创建对象,而无须知道具体的工厂类和对象。

抽象工厂模式的核心组成部分包括:
抽象工厂模式提供了一种封装一组相关对象的创建方式,便于对代码进行组织和管理;将客户端代码与具体工厂类和产品类解耦,从而使客户端代码更加灵活和可扩展。能够支持产品族的创建,可以创建一组相关的产品。

传统抽象工厂模式 UML 的简图如下图所示:


图 1 传统抽象工厂模式UML的简图

C++11实现抽象工厂模式的结构

传统方式的抽象工厂在需要增加产品时需要调整接口,增加新的工厂方法并实现其具体创建逻辑,这些工作都需要延迟到具体业务中。针对不同的业务场景需要进行重写或者修改代码。

通过 C++11 的元编程技术可以将这项工作独立出来,将创建通用的抽象工厂的相关逻辑实现在工厂模式的类模板中。这样业务程序员就可以将主要的注意力集中在业务实现上,使设计模式的逻辑和业务逻辑解耦。

使用元编程技术实现在编译期构造抽象工厂的具体类型,并和相关的产品类相关联。这种关联的关系是在编译期实现的,是一种静态的抽象工厂模式,并不会造成运行期的负担。

在具体实现中采用可变模板参数来添加产品类型;生产接口使用可变参数的函数模板,可以很灵活地使用不同类型的参数和参数数量,可以提高代码的灵活性和可复用性。

C++11 元编程抽象工厂模式 UML 的简图如下图所示:


图 2 C++11元编程抽象工厂模式UML的简图

这种实现方法并没有严格地实现抽象工厂模式的所有内容,但仍符合抽象工厂模式的基本特征,如工厂方法的使用。对于不同类型的对象,使用工厂模式模块中的模板函数 factory() 来创建相关的对象,这样客户端代码就不需要知道具体的实现细节了,从而实现了对象的解耦。这也提高了代码的可维护性和可扩展性。

抽象工厂模式的实现和解析

实际实现使用了两种方式,一种是宏;另一种是模板类:

1) 宏实现

宏实现方式主要通过宏来简化接口和实现两部分的编码方式,并在宏里面封装工厂方法。

先看主要代码的第一部分,这一部分中通过宏声明接口类的名称、基础的结构和接口方法定义的宏,在宏 DECLARE_ABSTRACT_FACTORY 的实现中,考虑了因为接口类会作为基类被实现类继承,所以在宏实现中包含一个空的虚析构函数,从而避免在业务中写接口类时忘记编写虚析构函数。

宏 ABST_PRODUCT_NAME 用来声明接口方法,这个宏直接将方法声明为纯虚函数,并在每个宏参数前增加“create_”前缀作为新的产品构造方法的名字。在宏中使用可变宏参数,因此一个宏用来声明不同参数表类型的接口。

__VA_ARGS__ 则是展开的宏参数,在实际使用这个宏时,可变宏参数是实际需要定义接口的参数表的数据类型,代码如下:
//designM/absFactory.h
#define DECLARE_ABSTRACT_FACTORY(absFactoryName)     \
class absFactoryName{                                \
public:                                              \
    virtual~absFactoryName(){}

#define END_DECLARE_ABSTRACT_FACTORY()};
//声明接口函数,利用可变宏展开可以很方便地实现任意参数数量的接口声明
#define ABST_PRODUCT_NAME(productName,...)           \
    virtual productName * create_#productName(__VA_ARGS__) = 0;

然后是代码的第二部分,这一部分实际上是针对第一部分的接口类型实现的宏工具。

宏 START_IMPL_ABST_FACTORY 用来声明接口实现类,宏参数 implFactoryName 是实现类的名字。实现类需要继承接口类 abstFactoryName,也就是使用上面声明接口宏声明的接口类,代码如下:
//designM/absFactory.h
START_IMPL_ABST_FACTORY(implFactoryName,abstFactoryName)
class implFactoryName:public abstFactoryName{
public:

#define END_IMPL_ABST_FACTORY()};

PRODUCT_NAME_XX 这个宏函数的操作是用来实现上面定义的虚函数接口,通过连接、可变宏参数实现灵活的函数声明和代码实现部分。在实际的实现代码中有 11 个相似的实现,它们的主要区别在于将 XX 替代为数字,具体的数字代表着可以使用的参数的数量。例如 0 代表实现的接口函数不需要参数,1 代表需要一个参数。

宏参数表里面 productType 是实际需要进行生产的产品类型,baseType 是实际需要返回的数据类型,但是进行内存分配时使用 productType,代码如下:
//designM/absFactory.h
#define PRODUCT_NAME_0(productType,baseType)  \
virtual baseType * create_#baseType()override  \
{                      \
    return factory< productType >();        \
}
#define PRODUCT_NAME_1(productType,baseType,param)  \
virtual baseType * create_#baseType(param p1)override    \
{
    return factory< productType >(p1);\
}
在这两部分代码中都使用了可变宏和可变宏的展开技巧,通过一个宏可以实现可变参数的接口函数声明。也用宏连接操作“#”在接口方法和实现方法上添加前缀。

在上述的实现中没有考虑内存安全,为了使用智能指针可以新增接口实现以智能指针管理的产品对象。将上面两部分代码修改为如下代码:
//designM/absFactory.h
#define ABST_PRODUCT_NAME_SHARED(productName, ...) \
virtual std::shared_ptr<productName > \
create_#productName(__VA_ARGS__) = 0;

#define PRODUCT_NAME_SHARED_0(productType, baseType ) \
virtual std::shared_ptr<baseType > \
create_#baseType() override \
\
{ \
   auto ret = factoryShared<productType >(); \
   if(ret){ \
       return std::dynamic_pointer_cast<baseType>(ret); \
   } \
   return {}; \
}

#define PRODUCT_NAME_SHARED_1(productType, baseType, param) \
virtual std::shared_ptr<baseType > \
create_#baseType(param p1) override \
{ \
   auto ret = factoryShared<productType >(p1); \
   if(ret) { \
       return std::dynamic_pointer_cast<baseType>(ret); \
   } \
   return {}; \
}
代码中使用工厂模式中的模板工厂函数 factoryShared<>() 来构造产品,这个工厂函数生成的对象以 std::shared_ptr<> 对象返回;std::dynamic_pointer_cast<>() 模板函数的功能是将原本的智能指针类型转换为指定的目标类型,这个模板函数要求源类型的智能指针和目标类型的智能指针存在继承关系,其内部实现是基于 dynamic_cast 操作符的。

2) 模板类实现方式

模板类的实现核心是利用 std::tuple 记录了所有产品类型信息,从而灵活地使用不同的产品类型,并利用不同的产品类的构造函数完成产品的生产操作。利用 std::tuple<> 模板类保存了产品类型的信息,后面根据实际的索引顺序创建需要的产品。

std::tuple<> 是 C++ 标准库中的一个模板类,用于存储一组不同类型的值,并以元组(tuple)的方式对其进行访问和操作。它类似于一个容器,可以将多个不同类型的值组合在一起,形成一个强类型集合,具体可以参考相关的文档。由于在 std::tuple<> 中保存了数据类型的相关信息,所以这个模块在远程中非常有用。

在下面的代码中使用可变的模板参数 productTypes,在使用模块时一次性地将所有的产品类型作为模板参数。这些模板参数将作为 std::tuple 的模板参数保存在 std::tuple 中,这里需要注意,所谓的将类型保存在 std::tuple 中是指在编译期保存在 std::tuple 中。

在下面的代码中有两个工厂方法 create() 和 createCallback,分别用于在构建裸指针的对象、构建裸指针类型对象后调用回调函数执行进一步的动作。

两个构建的函数均使用推导返回值的方法类确定函数的返回值类型。推导时实际上是根据 std::tuple 指定索引的类型进行处理的。函数的内部使用的是工厂模式的工厂函数方法,代码如下:
//designM/absFactory.h
template< typename...productTypes >
struct abstractFactory
{
     using prdtTypes = std::tuple< productTypes... >;

     template< size_t N,typename...Args >
     auto create(Args&&...args) -> typename std::tuple_element< N, prdtTypes >::type*
     {
          return factory< typename std::tuple_element< N, prdtTypes >::type >(std::forward<Args >(args)...);
     }
     template< size_t N,typename...Args >
     auto createCallback(std::function< void(std::tuple_element< N,prdtTypes >::type*) > func, Args&&...args)-> typename std::tuple_element< N,prdtTypes >::type*
     {
          auto * ret = factory<
          typename std::tuple_element< N,prdtTypes >::type>(std::forward<Args >(args)...);

          if(ret&&func){func(ret);}
          return ret;
    }
};

首先是代码的开头部分,代码如下:
template< typename...productTypes >
struct abstractFactory{
     using prdtTypes = std::tuple< productTypes... >;
     //...
};
这里定义了一个类模板 abstractFactory,它使用可变模板参数 productTypes 来表示可以生产的产品类型。prdtTypes 是一种类型别名,使用 std::tuple 将所有的产品类型管理起来。在代码中利用 std::tuple 包覆了类型表,方便后续使用序号推导出实际的类型。

然后是 create 函数的声明部分,代码如下:
template< size_t N,typename...Args >
auto create(Args&&...args) -> typename std::tuple_element< N, prdtTypes >::type*
{
     //...
}
这是一个模板函数的声明,它的模板参数包括 size_t N 和可变模板参数 Args。N 表示要创建的产品在元组中的索引位置,在使用时不需要显式传入;Args... 表示创建产品时需要传递的参数类型表。

create 函数是产品生产的方法,利用上一节实现的工厂函数实现了产品生产操作,代码如下:
return product<pdt_type >(std::forward<Args >(args)...);
这行代码的作用是调用 factory 函数来创建对应类型的产品实例。std::tuple_element<N,prdtTypes>::type 获取了在 prdtTypes 元组中索引为 N 的产品类型,然后将传入的 Args 参数以转发的方式传递给 factory 函数,这样就可以将参数正确地传递给相应类型的产品构造函数。

最后是 create 函数的返回语句,代码如下:
auto create(Args&&...args)->typename std::tuple_element<N,prdtTypes >::type*
{
   using pdt_type = typename std::tuple_element< N,prdtTypes >::type;
   return factory<pdt_type >(std::forward<Args >(args)...);
}

考虑生成对象后需要增加初始化的操作动作,可以在 create 函数参数中增加函数对象,将初始化动作放到函数对象中进行处理,代码如下:
template< size_t N,typename...Args >
auto create(
   std::function< void(std::tuple_element< N,prdtTypes >::type*,
   Args&&... > func,Args&&...args
)-> typename std::tuple_element< N,prdtTypes >::type*
{
   using pdt_type = typename std::tuple_element< N,prdtTypes >::type;
   pdt_type * ret = product<pdt_type >(std::forward<Args >(args)...);
   func(ret,std::forward<Args >(args)...);
   return ret;
}
模板函数 createShared<>() 用于需要返回 std::shared_ptr 的情况,以这种方式返回的产品对象会利用 std::shared_ptr 提供的内存管理功能,代码会更加安全。

在实际的实现模块中另外还支持 11 种参数数量的宏声明,用来提供更加丰富的宏接口,包含构建智能指针产品的 createShared() 方法和 createSharedCallback() 方法,提供智能指针对象的接口。

抽象工厂模式应用示例

1) 宏实现方式的示例,代码如下:
// absFactory.cpp
// 先定义需要的类
class a
{
public:
    a() { std::cout << "a" << std::endl; }
};
class b
{
public:
    b() { std::cout << "b" << std::endl; }
};
class a1 : public a
{
public:
    a1() { std::cout << "a1" << std::endl; }
};
class b1 : public b
{
public:
    b1(int p) { std::cout << "b1" << std::endl; }
};
class a2 : public a
{
public:
    a2() { std::cout << "a2" << std::endl; }
};
class b2 : public b
{
public:
    b2(int p) { std::cout << "b2" << std::endl; }
};

// 这一行声明了一个名字为 myAbsFactory 的抽象工厂
DECLARE_ABSTRACT_FACTORY(myAbsFactory)
    ABST_PRODUCT_NAME(a)    // 添加抽象产品类型 a
    // 添加抽象产品类型,初始化时需要提供一个 int 类型的参数
    ABST_PRODUCT_NAME(b, int)    // 结束抽象工厂声明
END_DECLARE_ABSTRACT_FACTORY()

实现抽象工厂 1。声明具体工厂 1,具体工厂是建立在 myAbsFactory 抽象工厂的基础之上的,代码如下:
START_IMPL_ABST_FACTORY(implF1, myAbsFactory)
    // 添加具体产品,具体产品建立在抽象产品之上
    PRODUCT_NAME_0(a1, a) // 还是需要指定初始化的数据类型
    PRODUCT_NAME_1(b1, b, int) // 结束具体工厂声明
END_IMPL_ABST_FACTORY()

实现抽象工厂 2,以同样的方式声明具体工厂 2,名字是 implF2:
START_IMPL_ABST_FACTORY(implF2, myAbsFactory)
    PRODUCT_NAME_0(a2, a)
    PRODUCT_NAME_1(b2, b, int)
END_IMPL_ABST_FACTORY()

int main(void)
{
    // 使用抽象工厂 myAbsFactory 定义一个工厂指针
    myAbsFactory * factory = nullptr;
    enum factoryType { f1, f2 }; // 提供一个选择的枚举对象
    factoryType f;
    f = f2; // 选择工厂 f2
    switch(f) {
        case f1: factory = new implF1; break; // 使用具体工厂 1
        case f2: factory = new implF2; // 使用具体工厂 2
    }

    // 使用抽象接口生产产品
    a * pA = factory->create_a();
    b * pB = factory->create_b(12);
    delete factory;
    delete pA;
    delete pB;
    return 0;
}
这段代码使用了前面提到的抽象工厂模式来创建具体产品。首先定义了 a 和 b 两个基类,并派生出具体产品 a1、a2、b1、b2,然后通过宏定义的方式声明了抽象工厂 myAbsFactory 及抽象产品 a 和 b。接下来使用另外两个宏定义的方式实现了具体工厂 implF1 和 implF2,并在其中分别指定了具体产品的映射关系。在 main() 函数中根据需要选择具体工厂的类型,并通过抽象工厂的指针调用对应地创建函数来创建产品。

运行这段代码,输出结果取决于具体选择的工厂类型,可以是以下两种情况中的一种。

使用具体工厂 implF1,输出如下:

a
a1
b
b1


使用具体工厂implF2,输出如下:

a
a2
b
b2


2) 使用模板类抽象工厂。假设有两种不同的产品:ProductA 和 ProductB,它们都是抽象基类 AbstractProduct 的派生类。现在需要创建一个工厂,根据输入的类型来创建相应的产品。

首先定义具体的产品类 ProductA 和 ProductB,它们都继承自 AbstractProduct,代码如下:
//absFactory2.cpp
class AbstractProduct{
public:
    virtual void printInfo() = 0;
};
class ProductA:public AbstractProduct{
public:
    void printInfo()override{
        std::cout<< "This is ProductA"<< std::endl;
    }
};
class ProductB:public AbstractProduct{
public:
    void printInfo()override{
        std::cout<< "This is ProductB"<< std::endl;
    }
};

接下来可以定义一个工厂类 MyFactory,通过继承 abstractFactory 来使用抽象工厂模式,代码如下:
//absFactory2.cpp
class MyFactory:public abstractFactory<ProductA,ProductB >{
public:
    template<typename...Args >
    static AbstractProduct * createProduct(int type,Args&&...args){
        if(type == 0)
             //对第1个产品执行创建动作
             return create<0 >(std::forward<Args >(args)...);
        else if(type == 1)
             //对第2个产品执行创建动作
             return create<1 >(std::forward<Args >(args)...);
        else
             return nullptr;
    }
};

在 MyFactory 中定义了一个静态成员函数 createProduct,它接收一种类型参数 type 和可变参数 args,并根据 type 的值来调用相应的产品创建函数。

现在可以使用 MyFactory 来创建不同类型的产品实例,代码如下:
//absFactory2.cpp
int main()
{
    AbstractProduct * productA = MyFactory::createProduct(0);
    productA->printInfo(); //输出:This is ProductA
    AbstractProduct * productB = MyFactory::createProduct(1);
    productB->printInfo(); //输出:This is ProductB
    delete productA;
    delete productB;
    return 0;
}
在上面的示例中通过调用 MyFactory::createProduct 并传入不同的类型参数来创建不同类型的产品实例,然后可以通过调用产品实例的成员函数来执行特定的操作。

相关文章