【问题标题】:Factory design pattern optimization [closed]工厂设计模式优化
【发布时间】:2018-09-06 21:05:18
【问题描述】:

我在 Creation.cc 和 Creation.h 中都有一个名为 Creation 的类,并且有一堆 createAcreateBcreateC...等。这个类里面的函数,都会返回一个指针类型。

我有很多modelA.cc、modelB.cc、modelC.cc ...等。文件,并且所有文件都包含 Creation.h。

由于每当我制作一个新的模型x(制作一个新的modelx.cc),我需要在Creation.h中添加对应的createx,这将使所有model.cc文件重新编译。

所有createAcreateBcreateC函数都有相同的参数列表,但不同的输入值和实现,基于它们的model.cc。

我的目标是在添加新的createx 函数时不想重新编译所有model.cc。

谢谢。

【问题讨论】:

  • 为什么你所有的类都依赖于工厂?
  • “优化”是什么意思?您的意思是尽量减少添加新模型所需的工作吗?还是您只是想避免重新编译?
  • @Galik 避免重新编译。谢谢!
  • 我正在寻找工厂方法。谢谢! @KennyOstrom
  • 欢迎。我不得不删除我原来的评论,因为我注意到后面的部分有些愚蠢。

标签: c++ design-patterns factory-pattern


【解决方案1】:

一个常见的策略是让工厂包含一个注册方法。一旦注册了一个类,就会调用工厂来获取一个实际的实例。

下面的 C++17 示例允许“接口”的子类在创建调用中具有不同的参数。 “createInstance”的任务是为特定类构造一个实例。

Any 类取自 here。如链接中所述, createInstance 调用对匹配方法签名的输入参数非常特别。

#include <iostream>
#include <functional>
#include <map>
#include <string>
#include <any>
#include <functional>
#include <map>
#include <string>
#include <iostream>

struct interface
{
};


template<typename Ret>
struct AnyCallable
{
    AnyCallable() {}
    template<typename F>
    AnyCallable(F&& fun) : AnyCallable(std::function(fun)) {}
    template<typename ... Args>
    AnyCallable(std::function<Ret(Args...)> fun) : m_any(fun) {}
    template<typename ... Args>
    Ret operator()(Args&& ... args) 
    { 
        return std::invoke(std::any_cast<std::function<Ret(Args...)>>(m_any), std::forward<Args>(args)...); 
    }
    std::any m_any;
};

struct factory
{
   std::map<std::string,  AnyCallable<interface>> registry;
   void registerClass(std::string const & class_name, AnyCallable<interface> function)
   {
      registry[class_name] = function;
   }

   template<typename ... Args>
   interface createInstance(std::string const & class_name, Args && ... args)
   {
      if(registry.find(class_name) == registry.end())
      {
         throw "No class found";
      }
      return registry[class_name](std::forward<Args>(args)...); 
   }
};


struct A:public interface
{
   A() 
   {
      std::cout << "Create A" << std::endl;
   }

   static interface createInstance(int t)
   {
      return A();
   } 

   static void registerWithFactory(factory& f)
   {
      f.registerClass("A",&createInstance);
   }
};


struct B:public interface
{
   B() 
   {
      std::cout << "Create B" << std::endl;
   }

   static interface createInstance(std::tuple<int, double> t)
   {
      return B();
   } 

   static void registerWithFactory(factory& f)
   {
      f.registerClass("B",&createInstance);
   }
};

int main(int argc, char* argv[])
{
    factory f;

    A::registerWithFactory(f);
    B::registerWithFactory(f);
    try {
        interface a = f.createInstance("A",1);
        interface b = f.createInstance("B",std::tuple{1,1.0});
        interface c = f.createInstance("C");
    }
    catch(...)
    {
       std::cout << "createInstance failed" << std::endl;
    }
    return 0;
}

工厂的所有成员都将从“界面”下降。 “工厂”将允许注册尚未创建的新类。在示例中,A 和 B 存在,但 C 不存在。将来可以在不重新编译现有代码的情况下添加 C。

有多种模式可以扩展此主题。

【讨论】:

  • 如果注册的函数不像A()和B(),但输入参数列表相同,但参数值和函数体不一样?
  • createInstance 可以是可变参数,stackoverflow.com/questions/1657883/… 或 std::bind 可用于在参数固定的情况下在注册表中创建条目。
  • 假设 B::CreateInstance() 就像 return CreateB(int, double,string),我可以在 main 函数中给那些 int double 和 string 的值吗?提前致谢。由于我是堆栈溢出的新手,因此系统似乎禁止我更改投票分数。
  • 我建议将标识符附加到类中,f.createInstance(A::id) 所以它只在一个地方。不错的演示,我现在就投赞成票。
  • 是的,关联的 id 很好。通常,创建是数据驱动的,但是传递数字/字符串很简单。例如,数据库中的一行包含确定其他列的业务逻辑的列,例如具有值“gzip”、“zip”、“raw”的列“compression”,可能会被馈送到 CompressorFactory。是不是这样实例类型可以在编译时完全未知,但当然受限于注册集。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-11-20
  • 2010-09-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多