C++ объясняет шаблон фабрики простыми словами (дополнительно)

Шаблоны проектирования
C++ объясняет шаблон фабрики простыми словами (дополнительно)

вводить

Первая статья предыдущей статьиC++ объясняет шаблон фабрики простыми словами (первое знакомство), в основном излагает структуру, характеристики и дефекты простого фабричного шаблона, фабричного метода и абстрактного фабричного шаблона. В приведенных выше трех методах при добавлении нового продукта либо измените класс фабрики, либо добавьте определенный класс фабрики, указывая на то, что инкапсуляция класса фабрики недостаточно хороша.

Расширенная часть этой статьи предназначена в основном для улучшения инкапсуляции фабричного класса.При добавлении новых продуктов нет необходимости изменять фабричный класс или добавлять конкретный фабричный класс. Фабричные классы с высокой степенью инкапсуляции характеризуются высокой масштабируемостью и высокой возможностью повторного использования.

фабрика шаблонов

Для шаблона фабричный метод он инкапсулирован в фабричный класс шаблона, поэтому при добавлении продукта нет необходимости добавлять конкретный фабричный класс, что сокращает объем написания кода.

UML-диаграмма:

Шаблон заводской код:
  • ShoesиClothe, абстрактные классы (базовые классы) для обуви и одежды соответственно
  • NiKeShoesиUniqloClothe, которые представляют собой определенные категории товаров для обуви Nike и одежды Uniqlo.
// 基类 鞋子
class Shoes
{
public:
    virtual void Show() = 0;
    virtual ~Shoes() {}
};

// 耐克鞋子
class NiKeShoes : public Shoes
{
public:
    void Show()
    {
        std::cout << "我是耐克球鞋,我的广告语:Just do it" << std::endl;
    }
};

// 基类 衣服
class Clothe
{
public:
    virtual void Show() = 0;
    virtual ~Clothe() {}
};

// 优衣库衣服
class UniqloClothe : public Clothe
{
public:
    void Show()
    {
        std::cout << "我是优衣库衣服,我的广告语:I am Uniqlo" << std::endl;
    }
};
  • AbstractFactoryЭто абстрактный класс фабрики шаблонов, где параметры шаблона:AbstractProduct_tАбстрактный класс продукта, напримерShoes,Clothe
  • ConcreteFactoryДля конкретного класса фабрики шаблонов, где параметры шаблона:AbstractProduct_tАбстрактный класс продукта (например,Shoes,Clothe),ConcreteProduct_tСпецифическая категория продукта (например,NiKeShoes,UniqloClothe)
// 抽象模板工厂类
// 模板参数:AbstractProduct_t 产品抽象类
template <class AbstractProduct_t>
class AbstractFactory
{
public:
    virtual AbstractProduct_t *CreateProduct() = 0;
    virtual ~AbstractFactory() {}
};

// 具体模板工厂类
// 模板参数:AbstractProduct_t 产品抽象类,ConcreteProduct_t 产品具体类
template <class AbstractProduct_t, class ConcreteProduct_t>
class ConcreteFactory : public AbstractFactory<AbstractProduct_t>
{
public:
    AbstractProduct_t *CreateProduct()
    {
        return new ConcreteProduct_t();
    }
};
  • mainфункция, в соответствии с различными типами продуктов, построить объект фабрики соответствующего продукта, а затем создать конкретный объект продукта через объект фабрики соответствующего продукта.
int main()
{
    // 构造耐克鞋的工厂对象
    ConcreteFactory<Shoes, NiKeShoes> nikeFactory;
    // 创建耐克鞋对象
    Shoes *pNiKeShoes = nikeFactory.CreateProduct();
    // 打印耐克鞋广告语
    pNiKeShoes->Show();

    // 构造优衣库衣服的工厂对象
    ConcreteFactory<Clothe, UniqloClothe> uniqloFactory;
    // 创建优衣库衣服对象
    Clothe *pUniqloClothe = uniqloFactory.CreateProduct();
    // 打印优衣库广告语
    pUniqloClothe->Show();

    // 释放资源
    delete pNiKeShoes;
    pNiKeShoes = NULL;

    delete pUniqloClothe;
    pUniqloClothe = NULL;

    return 0;
}
  • Выходной результат:
[root@lincoding factory]# ./templateFactory 
我是耐克球鞋,我的广告语:Just do it
我是优衣库衣服,我的广告语:I am Uniqlo

Класс шаблона регистрации продукта + класс шаблона singleton factory

Хотя предыдущая фабрика шаблонов не требует добавления определенного класса фабрики при добавлении продукта, в ней отсутствует класс, который может единообразно получать указанный объект продукта в любое время и в любом месте.

Есть еще возможности для улучшения, мы можем использовать объект регистрации продукта сstd::mapспособ сэкономить наkey-valveМетод может легко и просто получить соответствующий экземпляр объекта продукта.

Осознайте общую идею:

  • Функция регистрации продукта инкапсулирована в класс шаблона регистрации продукта. Зарегистрированный объект продукта хранится в классе шаблона фабрики.std::map, чтобы облегчить приобретение объектов продукта.

  • Инкапсулируйте функцию получения объектов продукта в класс фабричного шаблона. Чтобы получить указанный объект продукта в любое время и в любом месте, фабрика спроектирована как одноэлементный шаблон.

UML-диаграмма:

Регистрация продукта Шаблон класса + какая-то класс шаблона растений:
  • 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();
   }
};
  • mainфункцию, черезProductRegistrarЗарегистрируйте множество различных типов продуктов в единомProductFactoryОдноэлементная фабрика получает указанный объект продукта.
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;
}
  • Выходной результат:
[root@lincoding factory]# ./singleFactory 
我是耐克球鞋,我的广告语:Just do it
我是优衣库衣服,我的广告语:I am Uniqlo

Суммировать

Улучшите модель метода фабрики в фабрику шаблонов, хотя продукт может быть решен, нет необходимости добавлять определенные классы фабрики, но отсутствует способ получения объектов продукта в любое время и в любом месте, что указывает на то, что есть также улучшенное пространство.

Фабрика шаблонов улучшена в класс шаблона регистрации продукта + класс шаблона одноэлементной фабрики.Класс шаблона регистрации продукта используется для регистрации различных типов продуктов, а класс шаблона одноэлементной фабрики используется для получения указанного зарегистрированного объекта продукта. Таким образом, основные функции регистрации и получения продукта в фабричном шаблоне могут быть хорошо абстрагированы в два класса, а одноэлементный шаблон позволяет фабричному классу получать зарегистрированные объекты продукта в любое время и в любом месте.

Таким образом, фабричный шаблон класса шаблона регистрации продукта + одноэлементный класс шаблона фабрики реализует закон открытия-закрытия и обладает высокой масштабируемостью и высокой степенью инкапсуляции.

PS: если вы хотите узнать больше о шаблоне singleton, вы можете обратиться кРезюме поточно-ориентированного одноэлементного шаблона C++Чтение статьи.