工厂类的依赖注入

时间:2014-07-12 17:30:00

标签: c++ c++11 dependency-injection

我们有一个基于请求的应用程序,通过串行发送命令来执行请求。

当收到表明要执行请求的数据时,通过指定请求特定ID,使用工厂类创建请求。

问题是每个请求都有不同的依赖关系,具体取决于它必须执行的任务,我正在寻找最佳解决方案来实现这一目标。

当请求之间的依赖关系可能不同时,将依赖项注入请求的最佳方法是什么?

为每个可能的请求传递对RequestFactory的引用是不是一个坏主意? (我们目前有大约20-30个不同的请求,总共有大约6或7个不同的依赖项)

我计划的解决方案与下面的解决方案类似,但有更简单或更好的方法吗?

感谢。

class Request;
class RequestOne;
class RequestTwo;

class RequestFactory
{
public:
  RequestFactory( /* Dependencies for RequestOne and RequestTwo */ )
  {
    // Keep reference to dependencies
  }

  std::shared_ptr< Request > create( int id )
  {
    std::shared_ptr< Request > request;

    switch ( id )
    {
    case 1:
      request = std::make_shared< RequestOne >( /* RequestOne Dependencies */ );
      break;
    case 2:
      request = std::make_shared< RequestTwo >( /* RequestTwo Dependencies */ );
      break;
    }

    return request;
  }
};

class Request
{
public:
  virtual ~Request(  );
  virtual void process(  ) = 0;
};

class RequestOne : public Request
{
public:
  RequestOne( /* RequestOne Dependencies */ )
  virtual ~RequestOne(  );
  virtual void process(  );
};

class RequestTwo : public Request
{
public:
  RequestTwo( /* RequestTwo Dependencies */ );
  virtual ~RequestTwo(  );
  virtual void process(  );
};

2 个答案:

答案 0 :(得分:1)

听起来您主要关注需要提供给RequestFactory的构造函数参数的数量(即所有产品的依赖关系的并集)。您可以像处理类具有大量依赖关系的其他方式一样处理这种情况:识别类的新协作者

随着一个类收集越来越多的依赖项/协作者,模式往往会出现在某些依赖项之间。这些模式几乎总是代表一些以前未被识别的抽象。如果你可以在这样的抽象上放置一个名称,你可以重构该类以使用它来代替“相关的”依赖。

Mark Seemann将此称为Refactoring to Aggregate Services

你的RequestFactory似乎是一个很好的候选人。如果RequestFactory类与其他两个类合作,请考虑事物的外观:

class Request;
class RequestOne;
class RequestTwo;

class RequestOneFactory
{
public:
    virtual std::shared_ptr< RequestOne > CreateRequest(/* RequestOne Dependencies */) = 0;
};

class RequestTwoFactory
{
public:
    virtual std::shared_ptr< RequestTwo > CreateRequest(/* RequestTwo Dependencies */) = 0;
};

class RequestFactory
{
public:
  RequestFactory(std::shared_ptr< RequestOneFactory > requestOneFactory, std::shared_ptr< RequestTwoFactory > requestTwoFactory)
  {
    // Keep reference to collaborating factories
  }

  std::shared_ptr< Request > create( int id )
  {
    std::shared_ptr< Request > request;

    switch ( id )
    {
    case 1:
      request = requestOneFactory->CreateRequest();
      break;
    case 2:
      request = requestTwoFactory->CreateRequest();
      break;
    }

    return request;
  }
};

以这种方式看待事情,我们可能会开始怀疑RequestFactory是否实际承担了多个职责:

  1. 确定要创建的请求类型
  2. 创建该类型的请求
  3. 通过重构代码,RequestFactory在将另一个委托给协作类时保持第一责任。

    注意:使用上述方法,抽象工厂可能会受到具体请求类的影响,could be a code smell。但是,我怀疑RequestOneRequestTwo本身可能代表不同的抽象,这将使抽象工厂的引入更加符合逻辑。

答案 1 :(得分:1)

@Lilshieste提出的解决方案与原始实现具有相同的缺陷,您必须手动维护一个&#34; switch-heavy&#34;声明,甚至最糟糕的是,在@Lilshieste解决方案中,你已经增加了作为RequestFactory参数给出的工厂数量。

由于您没有提到任何性能问题,我建议使用更慢但更稳固的解决方案。

我不同意这需要聚合,原因是在您的情况下,您只是没有多个依赖项,您需要多态行为,这不需要聚合,而是构建适当的接口来处理。

观察1: 由于您使用的是C ++ 11,因此请使用新的枚举而不是&#34; int&#34;对于ID(这将产生有用的编译时错误)。

观察2: 扭转问题。不要让通用RequestFactory依赖于Concrete Factories,而是让ConcreteFactories在RequestFactory中注册自己!

class RequestOneFactory: public virtual AbstractRequestFactory{
    //note: no member variables! 
public:
    RequestOneFactory( std::shared_ptr<RequestFactorySupplier> factory){
        factory->register( getptr(),ID);
    }

    std::shared_ptr< Request> create() const{
        std::shared_ptr< Request> request =
            std::make_shared< RequestOne>( /* Dependencies*/);
        return request;
    }

};

每个工厂都有相同的接口,因为您正在进行DI,您可能希望管理工厂。只需从enable_shared_from_this继承。

class AbstractRequestFactory: std::enable_shared_from_this< AbstractRequestFactory>{
public:
    virtual ~AbstractRequestFactory(){}

    virtual std::shared_ptr< Request> create() const = 0;

    std::shared_ptr<AbstractRequestFactory> getptr() {
        return shared_from_this();
    }
};

RequestFactory现在没有构造函数依赖,同时请注意我将其界面分为两部分,以便工厂的不同用户可以做不同的事情。

#include <unordered_map>
#include <RequestFactorySupplier.hpp>
#include <RequestFactoryUser.hpp>
#include <AbstractRequestFactory.hpp>

class RequestFactory: public virtual RequestFactorySupplier
                     ,public virtual RequestFactoryUser{
    //associative container.
    std::unordered_map< RequestID, std::shared_ptr< AbstractRequestFactory>> map;

public:
    RequestFactory()=default;
    ~RequestFactory()=default;

    //IMPLEMENTS: RequestFactorySupplier
    virtual void register( std::shared_ptr< AbstractRequestFactory> fac, RequestID id){
         if(map.find(id)!=map.end()){
              //ID already used.. throw error, assert(false), what you want.
         }
         map[id] = fac;
    }

    //IMPLEMENTS: RequestFactoryUser
    virtual std::shared_ptr< Request> create(RequestID id){
         if(map.find(id)==map.end())
              throwSomething(); //No factory for such ID
         return map[id]->create();
    }
};

如果您使用的是依赖注入框架

现在连接所有内容的工作非常简单,下面的示例使用Infectorpp(我写过),当然,你可以用其他框架做类似的事情。

#include <Infectorpp/InfectorContainer.hpp>

int main(){
Infector::Container ioc;

//register generic factory with 2 interfaces
ioc.bindSingleAs<RequestFactory,    RequestFactorySupplier,RequestFactoryUser>();

//for each concrete factory register it
ioc.bindSingleAsNothing<RequestOneFactory>();
ioc.bindSingleAsNothing<RequestTwoFactory>();
//...

//wire the generic factory
ioc.wire<RequestFactory>();

//wire the factories (dependencies injected here)
ioc.wire<RequestOneFactory,    RequestFactorySupplier>();
ioc.wire<RequestTwoFactory,    RequestFactorySupplier>();
//...

//build the factories (let them register in RequestFactorySupplier)
{
    ioc.buildSingle<RequestOneFactory>();
    ioc.buildSingle<RequestTwoFactory>(); 
    //you will not have references to them but RequestFactory will: Good!
}

//Your classes can use RequestFactoryUser to create RequestObjects.

//application run!

return 0;
}

还考虑为那些工厂创建包围std :: unique_ptr的对象而不是std :: shared_ptr(如果unique_ptr足够,则永远不要使用std :: shared_ptr)。