我们有一个基于请求的应用程序,通过串行发送命令来执行请求。
当收到表明要执行请求的数据时,通过指定请求特定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( );
};
答案 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
是否实际承担了多个职责:
通过重构代码,RequestFactory
在将另一个委托给协作类时保持第一责任。
注意:使用上述方法,抽象工厂可能会受到具体请求类的影响,could be a code smell。但是,我怀疑RequestOne
和RequestTwo
本身可能代表不同的抽象,这将使抽象工厂的引入更加符合逻辑。
答案 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)。