正确实现依赖注入的一种方法是将对象创建与业务逻辑分开。通常,这涉及使用Factory进行对象创建。
到目前为止,我从未认真考虑过使用工厂,所以如果这个问题看起来有点过分,我会道歉:
在我遇到的工厂模式的所有示例中,我总是看到没有参数化的非常简单的示例。例如,这是从Misko Hevery's优秀How To Think About the "new" Operator文章中窃取的工厂。
class ApplicationBuilder { House build() { return new House(new Kitchen( new Sink(), new Dishwasher(), new Refrigerator()) ); } }
然而,如果我希望我建造的每个房子都有名字,会发生什么?如果我按如下方式重新编写此代码,我还在使用工厂模式吗?
class ApplicationBuilder { House build( const std::string & house_name) { return new House( house_name, new Kitchen(new Sink(), new Dishwasher(), new Refrigerator()) ); } }
请注意,我的Factory方法调用已更改为:
ApplicationBuilder builder; House * my_house = builder.build();
对此:
ApplicationBuilder builder; House * my_house = builder.build("Michaels-Treehouse");
顺便说一句:我认为将对象实例化与业务逻辑分离的概念很棒,我只想弄清楚如何将它应用于我自己的情况。令我困惑的是,我看到的Factory模式的所有示例都没有将任何参数传递给build()函数。
要明确:在我需要实例化它之前,我不知道房子的名称。
答案 0 :(得分:10)
我见过很多使用固定参数的例子,比如你的名字示例,并且我自己也使用了它们,我看不出它有什么问题。
然而,有很多理由说很多教程或小文章都避免显示工厂将参数转发给构造的对象:几乎无法转发任意数量的参数(即使对于理想的限制也是如此) 6个论点)。如果您想要通用的话,您转发的每个参数都必须被接受为const T&
和T&
。
但是,对于更复杂的示例,您需要一组指数增长的重载(对于每个参数,一个const和一个nonconst版本),并且perfect forwarding根本不可能(因此临时转发为临时,例如)。对于下一个解决问题的C ++标准:
class ApplicationBuilder {
template<typename... T>
House *build( T&&... t ) {
return new House( std::forward<T>(t)...,
new Kitchen(new Sink(),
new Dishwasher(),
new Refrigerator())
);
}
};
这样,您可以致电
builder.build("Hello", 13);
它将返回
new House("Hello", 13, new Kitchen(new Sink(...
阅读我上面链接的文章。
答案 1 :(得分:5)
我不明白为什么将此参数添加到工厂是错误的。但请注意,您最终不应添加许多参数,这些参数可能对工厂创建的所有对象都没有用。如果你这样做,你将失去很多工厂的优势!
答案 2 :(得分:5)
不仅可以接受,而且通用将参数传递给工厂方法。查看some examples。通常,参数是告诉工厂要做什么的类型,但是没有理由不能添加构建对象所需的其他信息。我觉得你做得很好。
答案 3 :(得分:4)
工厂的想法是它为您提供了类/接口的实例,因此传递参数没有任何问题。如果有,那么将参数传递给new()也是不好的。
答案 4 :(得分:1)
我同意Benoit的观点。想象一下工厂创建类似sql连接的东西,在这种情况下,有必要将有关连接的信息传递给工厂。工厂将使用该信息来使用正确的服务器协议等。
答案 5 :(得分:1)
当然,为什么不......!?
传递参数的好处是它允许您隐藏具体对象的实现。例如,在您发布的代码中,您将参数传递给构造函数。但是,您可以更改实现,以便通过 Initiailze 方法传递它们。通过将参数传递给工厂方法,可以隐藏从调用者构造和初始化对象的性质。
答案 6 :(得分:1)
看一下Loki :: Factory,但是它的实现非常类似于Boost。我经常使用不同风格的一些示例代码:
typedef Loki :: SingletonHolder&lt;洛基::工厂及LT; Component,std :: string,Loki :: Typelist&lt; const DataCollection&amp;,Loki :: Typelist&lt; Game *,Loki :: NullType&gt; &GT; &GT; &GT; ComponentFactory;
初看起来似乎有些奇怪,但让我解释一下这个野兽以及它究竟有多强大。基本上我们创建一个包含工厂的单例,大多数参数用于单例,Component是我们的产品,std :: string是我们的创建id类型,在此之后是创建组件所需的params的类型列表(这可以使用宏来定义,以获得更简洁的语法)。在这一行之后我们可以做到:
ComponentFactory :: Instance()。CreateObject(“someStringAssociatedWithConcreteType”,anDataCollection,aGamePointer);
要创建对象,要注册一个只使用ComponentFactory :: Instance()。Register();.在Modern C ++ Design一书中有一个很好的章节。