1.1 UML图

 MODEL: 鞋厂为了扩大了业务,不仅只生产鞋子,把运动品牌的衣服也一起生产了。

1.2 抽象工厂模式的结构组成

        1.抽象工厂类: 工厂方法模式的核心类,提供创建具体产品的接口,由具体工厂类实现。

        2.具体工厂类: 继承于抽象工厂,实现创建对应具体产品对象的方式。

        2.抽象产品类:它是具体产品继承的父类(基类)。

        3.具体产品类:具体工厂所创建的对象,就是此类。


1.3 抽象工厂模式的特点

        提供一个接口,可以创建多个产品族中的产品对象。如创建耐克工厂,则可以创建耐克鞋子产品、衣服产品、裤子产品等。

1.4 优缺点

        缺点:同工厂方法模式一样,新增产品时,都需要增加一个对应的产品的具体工厂类。

class Shoes   // 抽象产品类
{
    public:
    virtual void show() = 0;
    virtual ~Shoes() = default;
};

class Clothes  // 抽象产品类
{
    public:
    virtual ~Clothes() = default;
    virtual void show() = 0;
};

class NikeShoes : public Shoes   // 具体产品类
{
    public:
    void show()
    {
        // code
    }
};

class NikeClothes : public Clothes   // 具体产品类
{
    public:
    void show()
    {
        // code
    }
};

class Factory // 抽象工厂类
{
    public:
    virtual ~Factory() = default;
    virtual Shoes *createShoes() = 0;
    virtual Clothes *createClothes() = 0;
};

class NikeProduct : public Factory   // 具体工厂类
{
    public:
    Shoes *createShoes()
    {
        return new NikeShoes();
    }
    NikeClothes *createClothes()
    {
        return new NikeClothes();
    }
    
};


int main()
{
    Factory *nikeproduct = new NikeProduct();
    Shoes *nikeShoes = nikeproduct->createShoes();
    Clothes *nikeClothes = nikeproduct->createClothes();
    nikeShoes->show();
    delete nikeShoes;
    delete nikeShoes;
    delete nikeproduct;
    rreturn ;
}

以上三种工厂模式,在新增产品时,都存在一定的缺陷。

        简单工厂模式,需要去修改工厂类,这违背了开闭法则。

        工厂方式模式和抽象工厂模式,都需要增加一个对应的产品的具体工厂类,这就会增大了代码的编写量。

        那么有什么好的方法,在新增产品时,即不用修改工厂类,也不用新增具体的工厂。下面介绍一种改善的设计模式。

模板工厂

        针对工厂方法模式封装成模板工厂类,那么这样在新增产品时,是不需要新增具体的工厂类,减少了代码的编写量。

2.1 UML

class Shoes   // 抽象产品类
{
    public:
    virtual void show() = 0;
    virtual ~Shoes() = default;
};

class Clothes  // 抽象产品类
{
    public:
    virtual ~Clothes() = default;
    virtual void show() = 0;
};

class NikeShoes : public Shoes   // 具体产品类
{
    public:
    void show()
    {
        // code
    }
};

class NikeClothes : public Clothes   // 具体产品类
{
    public:
    void show()
    {
        // code
    }
};

template <class AbstractProduct_t>   // 模板参数AbstractProduct_t产品抽象类
class AbstractFactory             // 抽象模板工厂类
{
    public:
    virtual ~AbstractFactory() = default;
    virtual AbstractProduct_t *createProduct() = 0;
};

template<class AbstractProduct_t, class ConcreteProduct_t> 
// 模板参数:AbstractProduct_t 产品抽象类,ConcreteProduct_t 产品具体类
class ConcreteFactory : public AbstractFactory<AbstractProduct_t> // 具体模板工厂类
{
    public:
    AbstractProduct_t *createProduct()
    {
        return new ConcreteProduct_t();
    }
};

int main()
{
    ConcreteFactory<Shoes, NiKeShoes> nikeFactory;
    Shoes *nikeShoes = nikeFactory.CreateProduct();
    nikeShoes->show();
    delete nikeShoes;
    return 0;
}




        前面的模板工厂虽然在新增产品的时候,不需要新增具体的工厂类,但是缺少一个可以统一随时随地获取指定的产品对象的类。

        还有改进的空间,我们可以把产品注册的对象用std::map的方式保存,通过key-valve的方式可以轻松简单的获取对应的产品对象实例。

实现大致思路:

        把产品注册的功能封装成产品注册模板类。注册的产品对象保存在工厂模板类的std::map,便于产品对象的获取。

        把获取产品对象的功能封装成工厂模板类。为了能随时随地获取指定产品对象,则把工厂设计成单例模式。

3.1UML

        IProductRegistrar为产品注册抽象类,模板参数 ProductType_t表示的类是产品抽象类(如Shoes、Clothe)。提供了产品对象创建的纯虚函数CreateProduct。

        ProductFactory为工厂模板类,模板参数 ProductType_t表示的类是产品抽象类(如Shoes、Clothe)。用于保存注册产品对象到std::map中和获取对应的产品对象。

        ProductRegistrar为产品注册模板类,模板参数 ProductType_t表示的类是产品抽象类(如Shoes、Clothe),ProductImpl_t 表示的类是具体产品(如NikeShoes、UniqloClothe)。用于注册产品到工厂类和创建产品实例对象。

// 基类,产品注册模板接口类
// 模板参数 ProductType_t 表示的类是产品抽象类
template <class ProductType_t>
class IProductRegistrar
{
public:
   // 获取产品对象抽象接口
   virtual ProductType_t *CreateProduct() = 0;

protected:
   // 禁止外部构造和虚构, 子类的"内部"的其他函数可以调用
   IProductRegistrar() {}
   virtual ~IProductRegistrar() {}

private:
   // 禁止外部拷贝和赋值操作
   IProductRegistrar(const IProductRegistrar &);
   const IProductRegistrar &operator=(const IProductRegistrar &);
};

// 工厂模板类,用于获取和注册产品对象
// 模板参数 ProductType_t 表示的类是产品抽象类
template <class ProductType_t>
class ProductFactory
{
public:
   // 获取工厂单例,工厂的实例是唯一的
   static ProductFactory<ProductType_t> &Instance()
   {
      static ProductFactory<ProductType_t> instance;
      return instance;
   }

   // 产品注册
   void RegisterProduct(IProductRegistrar<ProductType_t> *registrar, std::string name)
   {
      m_ProductRegistry[name] = registrar;
   }

   // 根据名字name,获取对应具体的产品对象
   ProductType_t *GetProduct(std::string name)
   {
      // 从map找到已经注册过的产品,并返回产品对象
      if (m_ProductRegistry.find(name) != m_ProductRegistry.end())
      {
         return m_ProductRegistry[name]->CreateProduct();
      }

      // 未注册的产品,则报错未找到
      std::cout << "No product found for " << name << std::endl;

      return NULL;
   }

private:
   // 禁止外部构造和虚构
   ProductFactory() {}
   ~ProductFactory() {}

   // 禁止外部拷贝和赋值操作
   ProductFactory(const ProductFactory &);
   const ProductFactory &operator=(const ProductFactory &);

   // 保存注册过的产品,key:产品名字 , value:产品类型
   std::map<std::string, IProductRegistrar<ProductType_t> *> m_ProductRegistry;
};

// 产品注册模板类,用于创建具体产品和从工厂里注册产品
// 模板参数 ProductType_t 表示的类是产品抽象类(基类),ProductImpl_t 表示的类是具体产品(产品种类的子类)
template <class ProductType_t, class ProductImpl_t>
class ProductRegistrar : public IProductRegistrar<ProductType_t>
{
public:
   // 构造函数,用于注册产品到工厂,只能显示调用
   explicit ProductRegistrar(std::string name)
   {
      // 通过工厂单例把产品注册到工厂
      ProductFactory<ProductType_t>::Instance().RegisterProduct(this, name);
   }

   // 创建具体产品对象指针
   ProductType_t *CreateProduct()
   {
      return new ProductImpl_t();
   }
};

int main()
{
   // ========================== 生产耐克球鞋过程 ===========================//
   // 注册产品种类为Shoes(基类),产品为NiKe(子类)到工厂,产品名为nike
   ProductRegistrar<Shoes, NiKeShoes> nikeShoes("nike");
   // 从工厂获取产品种类为Shoes,名称为nike的产品对象
   Shoes *pNiKeShoes = ProductFactory<Shoes>::Instance().GetProduct("nike");
   // 显示产品的广告语
   pNiKeShoes->Show();
   // 释放资源
   if (pNiKeShoes)
   {
      delete pNiKeShoes;
   }

   // ========================== 生产优衣库衣服过程 ===========================//
   // 注册产品种类为Clothe(基类),产品为UniqloClothe(子类)到工厂,产品名为uniqlo
   ProductRegistrar<Clothe, UniqloClothe> adidasShoes("uniqlo");
   // 从工厂获取产品种类为Shoes,名称为adidas的产品对象
   Clothe *pUniqloClothe = ProductFactory<Clothe>::Instance().GetProduct("uniqlo");
   // 显示产品的广告语
   pUniqloClothe->Show();
   // 释放资源
   if (pUniqloClothe)
   {
      delete pUniqloClothe;
   }

   return 0;
}

        将工厂方法模式改良成模板工厂,虽然可以解决产品新增时,不需要新增具体工厂类,但是缺少一个可以随时随地获取产品对象的方式,说明还有改进的空间。

        将模板工厂改良成产品注册模板类+单例工厂模板类,产品注册模板类用于注册不同类型的产品,单例工厂模板类用于获取指定已注册的产品对象。这种方式,可以把工厂模式中产品的注册和获取的主要功能很好的抽象成两个类,并且使用单例模式使得工厂类可以随时随地获取已注册的产品对象。

        所以产品注册模板类+单例工厂模板类的工厂模式,达到了开闭法则,并且扩展性高和封装度高。

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐