introduce

The structure, characteristics and defects of simple factory pattern, factory method pattern and abstract factory pattern are mainly described in the initial part of C++. In the above three ways, when adding products, factory class should be modified or specific factory class should be added, indicating that the packaging of factory class is not good enough.

In the advanced part of this paper, the encapsulation of factory class is improved. When new products are added, there is no need to modify factory class or add specific factory class. The factory class with high encapsulation is characterized by high scalability and high reusability.

Template factory

The factory method pattern is encapsulated into template factory class, so when adding products, there is no need to add specific factory class, reducing the amount of code writing.

UML diagrams:

Template factory code:
  • ShoesandClothe, are the abstract classes for shoes and clothes (base class)
  • NiKeShoesandUniqloClotheAre specific product categories of Nike shoes and Uniqlo clothing respectively.
// Base shoes
class Shoes
{
public:
    virtual void Show(a) = 0;
    virtual ~Shoes() {}
};

// Nike shoes
class NiKeShoes : public Shoes
{
public:
    void Show(a)
    {
        std: :cout << "I'm a Nike sneaker, my slogan: Just do it." << std: :endl; }};// Basic clothes
class Clothe
{
public:
    virtual void Show(a) = 0;
    virtual ~Clothe() {}
};

// Uniqlo clothes
class UniqloClothe : public Clothe
{
public:
    void Show(a)
    {
        std: :cout << "I'm Uniqlo, my slogan: I am Uniqlo." << std: :endl; }};Copy the code
  • AbstractFactoryIs the abstract template factory class, where template parameters:AbstractProduct_tProduct abstract classes, such asShoes,Clothe
  • ConcreteFactoryIs the concrete template factory class, where template parameters:AbstractProduct_tProduct abstract classes (e.gShoes,Clothe),ConcreteProduct_tProduct specific classes (e.gNiKeShoes,UniqloClothe)
// Abstract template factory class
// Template argument: AbstractProduct_t Product abstract class
template <class AbstractProduct_t>
class AbstractFactory
{
public:
    virtual AbstractProduct_t *CreateProduct(a) = 0;
    virtual ~AbstractFactory() {}
};

// Concrete template factory class
AbstractProduct_t abstract class, ConcreteProduct_t concrete class
template <class AbstractProduct_t.class ConcreteProduct_t>
class ConcreteFactory : public AbstractFactory<AbstractProduct_t>
{
public:
    AbstractProduct_t *CreateProduct(a)
    {
        return newConcreteProduct_t(); }};Copy the code
  • mainFunction, according to different types of products, to construct the corresponding product factory object, can create a specific product object from the corresponding product factory object.
int main(a)
{
    // The factory object that constructs Nike shoes
    ConcreteFactory<Shoes, NiKeShoes> nikeFactory;
    // Create a Nike shoe object
    Shoes *pNiKeShoes = nikeFactory.CreateProduct();
    // Print the slogan for Nike shoes
    pNiKeShoes->Show();

    // The factory object that constructs uniqlo clothes
    ConcreteFactory<Clothe, UniqloClothe> uniqloFactory;
    // Create a Uniqlo clothes object
    Clothe *pUniqloClothe = uniqloFactory.CreateProduct();
    // Print the uniqlo slogan
    pUniqloClothe->Show();

    // Release resources
    delete pNiKeShoes;
    pNiKeShoes = NULL;

    delete pUniqloClothe;
    pUniqloClothe = NULL;

    return 0;
}
Copy the code
  • Output result:
[root@lincoding factory]#./templateFactory I am Nike, my slogan is: Just do it I am UniqloCopy the code

Product registration template class + singleton factory template class

The previous template factory does not need to add a specific factory class when adding products, but it lacks a class that can uniformly get specified product objects anytime and anywhere.

There is still room for improvement. We can save the product registered object as STD ::map, and obtain the corresponding product object instance easily through key-valve.

General idea:

  • Encapsulate the functionality of product registration as a product registration template class. Registered product objects are stored in the factory template class STD :: Map for easy product object retrieval.

  • Encapsulate the ability to get a product object as a factory template class. The factory is designed as a singleton pattern in order to obtain the specified product object anytime and anywhere.

UML diagrams:

Product Registration template + singleton factory template class:
  • IProductRegistrarRegister abstract classes, template parameters for the productProductType_tThe classes represented are product abstract classes (e.gShoes,Clothe). Provides pure virtual functions for product object creationCreateProduct.
  • ProductFactoryIs the factory template class, template parameterProductType_tThe classes represented are product abstract classes (e.gShoes,Clothe). Used to save registered product objects tostd::mapAnd get the corresponding product object.
  • ProductRegistrarRegister the template class for the product, template parametersProductType_tThe classes represented are product abstract classes (e.gShoes,Clothe),ProductImpl_tThe classes represented are concrete products (e.gNikeShoes,UniqloClothe). Use to register products into factory classes and create product instance objects.
// Base class, product registration template interface class
// The template parameter ProductType_t represents a product abstract class
template <class ProductType_t>
class IProductRegistrar
{
public:
   // Get the product object abstract interface
   virtual ProductType_t *CreateProduct(a) = 0;

protected:
   // Prohibit external construction and fabrication, other functions of the "inside" of the subclass can be called
   IProductRegistrar() {}
   virtual ~IProductRegistrar() {}

private:
   // Disallow external copy and assignment operations
   IProductRegistrar(const IProductRegistrar &);
   const IProductRegistrar &operator= (const IProductRegistrar &);
};

// Factory template class to get and register product objects
// The template parameter ProductType_t represents a product abstract class
template <class ProductType_t>
class ProductFactory
{
public:
   // Get the factory singleton. The factory instance is unique
   static ProductFactory<ProductType_t> &Instance()
   {
      static ProductFactory<ProductType_t> instance;
      return instance;
   }

   // Product registration
   void RegisterProduct(IProductRegistrar<ProductType_t> *registrar, std: :string name)
   {
      m_ProductRegistry[name] = registrar;
   }

   // Obtain the corresponding product object according to the name name
   ProductType_t *GetProduct(std: :string name)
   {
      // Find the registered product from map and return the product object
      if(m_ProductRegistry.find(name) ! = m_ProductRegistry.end()) {return m_ProductRegistry[name]->CreateProduct();
      }

      // If the product is not registered, an error is reported
      std: :cout << "No product found for " << name << std: :endl;

      return NULL;
   }

private:
   // Prohibit external constructs and fictions
   ProductFactory() {}
   ~ProductFactory() {}

   // Disallow external copy and assignment operations
   ProductFactory(const ProductFactory &);
   const ProductFactory &operator= (const ProductFactory &);

   // Save the registered product, key: product name, value: product type
   std: :map<std: :string, IProductRegistrar<ProductType_t> *> m_ProductRegistry;
};

// Product registration template class, used to create concrete products and register products from the factory
// The template argument ProductType_t represents the product abstract class (base class), and the ProductImpl_t represents the concrete product (a subclass of the product category).
template <class ProductType_t.class ProductImpl_t>
class ProductRegistrar : public IProductRegistrar<ProductType_t>
{
public:
   // Constructor, used to register products to factories, can only display calls
   explicit ProductRegistrar(std: :string name)
   {
      // Register the product to the factory through the factory singleton
      ProductFactory<ProductType_t>::Instance().RegisterProduct(this, name);
   }

   // Create a pointer to a specific product object
   ProductType_t *CreateProduct(a)
   {
      return newProductImpl_t(); }};Copy the code
  • mainDelta function, byProductRegistrarRegister a variety of different types of products in the unified byProductFactoryThe singleton factory gets the specified product object.
int main(a)
{
   / / = = = = = = = = = = = = = = = = = = = = = = = = = = production Nike sneakers = = = = = = = = = = = = = = = = = = = = = = = = = = = / /
   // The registered product category is Shoes (base class), the product is NiKe (subclass) to the factory, the product name is NiKe
   ProductRegistrar<Shoes, NiKeShoes> nikeShoes("nike");
   // Obtain the product object with the category of Shoes and the name of Nike from the factory
   Shoes *pNiKeShoes = ProductFactory<Shoes>::Instance().GetProduct("nike");
   // Display the slogan of the product
   pNiKeShoes->Show();
   // Release resources
   if (pNiKeShoes)
   {
      delete pNiKeShoes;
   }

   / / = = = = = = = = = = = = = = = = = = = = = = = = = = uniqlo clothing production process = = = = = = = = = = = = = = = = = = = = = = = = = = = / /
   // The registered product category is Clothe (base class), the product is UniqloClothe (subclass) to factory, the product name is UniqLO
   ProductRegistrar<Clothe, UniqloClothe> adidasShoes("uniqlo");
   // Get a product object with the product type Shoes and the name adidas from the factory
   Clothe *pUniqloClothe = ProductFactory<Clothe>::Instance().GetProduct("uniqlo");
   // Display the slogan of the product
   pUniqloClothe->Show();
   // Release resources
   if (pUniqloClothe)
   {
      delete pUniqloClothe;
   }

   return 0;
}
Copy the code
  • Output result:
[root@lincoding Factory]#./singleFactory I am Nike, my slogan: Just do it I am UniqloCopy the code

conclusion

The modification of factory method pattern into template factory can solve the problem of new products without the need to add specific factory classes, but the lack of a way to obtain product objects anytime and anywhere shows that there is still room for improvement.

The template factory is modified to a product registration template class for registering different types of products and a singleton factory template class for getting specified registered product objects. In this way, the main functions of product registration and acquisition in the factory pattern are abstracted into two classes, and the singleton pattern is used to enable the factory class to obtain registered product objects anytime and anywhere.

Therefore, the factory mode of product registration template class + singleton factory template class achieves the open and close law, and has high scalability and high encapsulation degree.

PS: to learn more about the singleton pattern, see the summary of the C++ thread-safe singleton pattern.