• Modern C++ 模板通用工厂


    Modern C++ 模板通用工厂

    1 简单应用示例

    1.1 示例代码

    #include <iostream>
    #include <string>
    using namespace std;
    
    class Shape
    {
    public:
        virtual void calc_area() { cout << "call Shape::calc_area" << endl; }
    };
    
    class Circle : public Shape
    {
    public:
        virtual void calc_area() { cout << "call Circle::calc_area" << endl; }
    };
    
    class Square : public Shape
    {
    public:
        virtual void calc_area() { cout << "call Square::calc_area" << endl; }
    };
    
    class Rectangle : public Shape
    {
    public:
        virtual void calc_area() { cout << "call Rectangle::calc_area" << endl; }
    };
    
    int main()
    {
        Shape* pB = new Shape;
        pB->calc_area();  //call Shape::calc_area
        delete pB;
    
        Shape* pC = new Circle;
        pC->calc_area(); //call Circle::calc_area
        delete pC;
    
        Shape* pS = new Square;
        pS->calc_area(); //call Square::calc_area
        delete pS;
    
        Shape* pR = new Rectangle;
        pR->calc_area(); //call Rectangle::calc_area
        delete pR;
    
        system("pause");
    }
    
    

    1.2 分析说明

    1. 缺点:
      • 每种类型对象在使用时候自己负责创建
      • 子类多的话,代码很长
    2. 优点:运行时多态

    2 简单工厂模式

    2.1 示例代码

    #include <iostream>
    #include <string>
    using namespace std;
    
    class Shape
    {
    public:
        virtual void calc_area() { cout << "call Shape::calc_area" << endl; }
    };
    
    class Circle : public Shape
    {
    public:
        virtual void calc_area() { cout << "call Circle::calc_area" << endl; }
    };
    
    class Square : public Shape
    {
    public:
        virtual void calc_area() { cout << "call Square::calc_area" << endl; }
    };
    
    class Rectangle : public Shape
    {
    public:
        virtual void calc_area() { cout << "call Rectangle::calc_area" << endl; }
    };
    
    class ShapeFactory
    {
    public:
        static Shape* createShape(std::string type)
        {
            if ("circle" == type)
            {
                return new Circle;
            }
            if ("square" == type)
            {
                return new Square;
            }
            if ("rectangle" == type)
            {
                return new Rectangle;
            }
            return nullptr;
        }
    };
    
    int main()
    {
        Shape* ptr = ShapeFactory::createShape("circle");
        if (ptr != nullptr)
        {
            ptr->calc_area();  //call Circle::calc_area
            delete ptr;
        }
    
        system("pause");
    }
    
    

    2.2 分析说明

    1. 缺点
      • 每新增一种类型,都需要写个新类型的if-else(或switch-case)
    2. 优点
      • 使用工厂统一创建每种类型的对象

    3 工厂 + 静态注册

    3.1 示例代码

    #include <string>
    #include <iostream>
    #include <unordered_map>
    using namespace std;
    
    class Shape
    {
    public:
        virtual void calc_area() { cout << "call Shape::calc_area" << endl; }
    };
    
    class Circle : public Shape
    {
    public:
        virtual void calc_area() { cout << "call Circle::calc_area" << endl; }
    };
    
    class Square : public Shape
    {
    public:
        virtual void calc_area() { cout << "call Square::calc_area" << endl; }
    };
    
    class Rectangle : public Shape
    {
    public:
        virtual void calc_area() { cout << "call Rectangle::calc_area" << endl; }
    };
    
    typedef void* (*pCreateShape)(void);
    
    class CShapeFactory
    {
    public:
        ~CShapeFactory() = default;
    
    public:
        void* getShapeByName(const std::string& className)
        {
            auto iter = m_mapCShape.find(className);
            if (iter == m_mapCShape.end())
                return nullptr;
    
            return iter->second();
        }
    
        void registoryClass(const std::string& name, pCreateShape method) 
        {
            m_mapCShape.insert(make_pair(name, method));
        }
    
        static CShapeFactory& getInstance()
        {
            static CShapeFactory oFactory;
            return oFactory;
        }
    
    private:
        CShapeFactory() {};
    
        std::unordered_map<std::string, pCreateShape> m_mapCShape;  // 引入map
    };
    
    // 注册动作类
    class CRegisterAction
    {
    public:
        CRegisterAction(std::string className, pCreateShape pCreateFn)
        {
            CShapeFactory::getInstance().registoryClass(className, pCreateFn);
        }
    };
    
    #define REGISTER_SERVICE(className)                      \
        className* create##className() {                     \
            return new className;                            \
        }                                                    \
        CRegisterAction createRegister##className(           \
            #className, (pCreateShape)create##className)
    
    
    REGISTER_SERVICE(Circle);
    REGISTER_SERVICE(Square);
    REGISTER_SERVICE(Rectangle);
    
    int main()
    {
        for (auto& item : { "Circle", "Square", "Rectangle" })
        {
            auto pShape = (Shape*)CShapeFactory::getInstance().getShapeByName(item);
            if (pShape != nullptr)
            {
                pShape->calc_area();
                delete pShape;
            }
        }
    
        system("pause");
    }
    
    /* result:
    call Circle::calc_area
    call Square::calc_area
    call Rectangle::calc_area
    请按任意键继续. . .
    */
    

    3.2 分析说明

    1. 缺点
      • 每个类都需要注册,容易遗漏
    2. 优点
      • 优化掉了if-else和switch-case

    4 CRTP工厂

    4.1 示例代码

    1. factory.hpp

      #pragma once
      #include <memory>
      #include <functional>
      #include <unordered_map>
      #include <utility>
      
      // ref: http://www.nirfriedman.com/2018/04/29/unforgettable-factory/
      
      namespace prefab
      {
          namespace patterns
          {
              template<typename I, typename Identify, typename... Args>
              class factory
              {
                  friend I;
      
                  using K = decltype(Identify::key);  //typename Identify::key_type;
                  using V = decltype(Identify::value);// typename Identify::value_type;
      
                  struct key 
                  {
                      key() {};
                      template<typename T>
                      friend struct registar;
                  };
      
                  factory() = default;
      
                  using builder = std::function<std::unique_ptr<I>(Args...)>;
                  using reader = V(*)();
      
                  static auto& builders()
                  {
                      static std::unordered_map<K, builder> container;
                      return container;
                  }
      
                  static auto& readers()
                  {
                      static std::unordered_map<K, reader> container;
                      return container;
                  }
              public:
                  using self = factory<I, Identify, Args...>;
      
                  template<typename... Ts>
                  static std::unique_ptr<I> make(K const& k, Ts&&... args)
                  {
                      auto it = builders().find(k);
                      if (it == builders().end()) return nullptr;
                      return it->second(std::forward<Ts>(args)...);
                  }
      
                  static std::pair<bool, V> identify(K const& k)
                  {
                      std::pair<bool, V> result;
                      result.first = false;
                      auto it = readers().find(k);
                      if (it == readers().end()) return result;
                      result.first = true;
                      result.second = it->second();
                      return result;
                  }
      
                  template<typename T>
                  struct registar : I
                  {
                      friend T;
      
                      static bool register_()
                      {
                          const auto r = T::identify().key;
      
                          factory::builders()[r] = [](Args&&... args)->std::unique_ptr<I> {
                              return std::make_unique<T>(std::forward<Args>(args)...);
                          };
      
                          factory::readers()[r] = []()->V {
                              return T::identify().value;
                          };
                          return true;
                      }
      
                      static bool registered;
                  private:
                      registar() : I(key{})
                      {
                          (void)registered;
                      }
                  };
              };
      
              template<typename I, typename Identify, typename... Args>
              template<typename T>
              bool factory<I, Identify, Args...>::registar<T>::registered =
                  factory<I, Identify, Args...>::registar<T>::register_();
          }
      }
      
      #if 0
      
      #include <iostream>
      #include <string>
      
      namespace prefab_patterns_example
      {
          template<typename I, typename Identify, typename... Args>
          using Factory = prefab::patterns::factory<I, Identify, Args...>;
      
          struct task_identify
          {
              std::string key;
              std::string value;
          };
      
          struct task :public Factory<task, task_identify, int>
          {
              task(self::key) {};
              virtual void execute() = 0;
          };
      
          struct print :task::registar<print>
          {
              print(int) {};
      
              void execute()override {
                  std::cout << "print\n";
              };
      
              static task_identify identify() noexcept {
                  (void)registered;  // MSVC has bug,use this force register
                  return task_identify{ "print","task_print" };
              }
          };
      }
      
      #endif
      
    2. CShapeFactory.hpp

      #include <string>
      #include <iostream>
      #include "factory.hpp"
      
      template<typename I, typename Identify, typename... Args>
      using Factory = prefab::patterns::factory<I, Identify, Args...>;
      
      struct IShape_identify
      {
          std::string key;
          std::string value;
      };
      
      struct IShape : public Factory<IShape, IShape_identify>
      {
          IShape(self::key) {};
          virtual void calc_area() = 0;
      };
      
      struct Circle : public IShape::registar<Circle>
      {
      public:
          Circle() {}
      
          virtual void calc_area() { std::cout << "call Circle::calc_area" << std::endl; }
      
          static IShape_identify identify() noexcept {
              return IShape_identify{ "C", "Circle" };
          }
      };
      
      struct Square : public IShape::registar<Square>
      {
      public:
          Square() {}
      
          virtual void calc_area() { std::cout << "call Square::calc_area" << std::endl; }
      
          static IShape_identify identify() noexcept {
              return IShape_identify{ "S", "Square" };
          }
      };
      
      struct Rectangle : public IShape::registar<Rectangle>
      {
      public:
          Rectangle() {}
      
          virtual void calc_area() { std::cout << "call Rectangle::calc_area" << std::endl; }
      
          static IShape_identify identify() noexcept {
              return IShape_identify{ "R", "Rectangle" };
          }
      };
      
    3. main.cpp

      #include "CShapeFactory.hpp"
      
      int main()
      {
          for (auto& item : { "C", "S", "R" })
          {
              auto target = IShape::make(item);
              if (target != nullptr)
              {
                  target->calc_area();
              }
          }
      
          system("pause");
      }
      
      /* result:
      call Circle::calc_area
      call Square::calc_area
      call Rectangle::calc_area
      请按任意键继续. . .
      */
      

    4.2 分析说明

    关于CRTP,详见随笔《C++ CRTP》

    4.3 过程中问题

    1. 编译错误:C7510 “registar”: 模板从属名称的使用必须以“模板”为前缀

      解决方案:本地VS2019,关闭工程的“符合模式”即可(默认开启),如下图:

    作者:kaizen
    声明:本文版权归作者和博客园共有,欢迎转载。但未经作者同意必须保留此声明,且在文章明显位置给出本文链接,否则保留追究法律责任的权利。
    签名:顺序 选择 循环
  • 相关阅读:
    开启sftp服务日志并限制sftp访问目录
    Django-16-安装前端项目
    sql
    Django-15-用户模块、认证授权、session会话认证和token认证
    Django-14-项目工程搭建
    开发小技巧
    Django-13-类视图设计原则
    Django-11-自动生成routers路由、自定义action
    Django-9-序列化器中各种校验方式
    Django—问题—生成迁移脚本时报错:You are trying to add a non-nullable field 'gender' to interfaces without a default
  • 原文地址:https://www.cnblogs.com/Braveliu/p/15669861.html
Copyright © 2020-2023  润新知