工厂方法能否接受参数?

13
实现依赖注入的正确方法之一是将对象创建与业务逻辑分开。通常,这涉及使用工厂模式进行对象创建。
到目前为止,我从未认真考虑过使用工厂模式,因此如果这个问题似乎有点简单,我表示歉意:
在我看到的所有工厂模式示例中,我都只看到了没有参数化的非常简单的示例。例如,这里是一个从 Misko Hevery 的优秀文章《如何思考“new”运算符》中窃取的工厂:
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())
                    );
  }
}
请注意,我的工厂方法调用已从:
ApplicationBuilder builder;
House * my_house = builder.build();
变为:
ApplicationBuilder builder;
House * my_house = builder.build("Michaels-Treehouse");
顺便说一句:我认为将对象实例化与业务逻辑分开的概念很棒,我只是试图弄清楚如何将它应用于自己的情况。令我困惑的是,我看到的所有工厂模式示例都没有将任何参数传递到 build() 函数中。
请明确:在实例化之前的那一刻,我不知道房子的名称。

我经常看到工厂像你展示的那样接受参数。这没有任何问题。或者从你的问题来看,这是完全可以的。 - grieve
7个回答

13

我看过很多使用固定参数集的示例,比如你名字的例子,我自己也用过,并且我认为这种方法没有什么问题。

然而,许多教程或小文章避免展示将参数传递给构造对象的工厂的好原因是:实际上不可能传递任意数量的参数(即使是对于一个合理的限制,例如6个参数)。每个您转发的参数都必须被接受为const T&T&(如果要进行通用操作)。

然而,对于更复杂的示例,您需要成倍增长的重载集(对于每个参数,都有一个const和nonconst版本),并且根本不可能进行完美转发(例如将临时对象作为临时对象转发)。对于下一个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(...

请阅读我上面链接的文章。

关于右值引用的好链接!这可能超出了问题的范围,但这是我读过的最清晰的文章之一。 - Luc Touraille

9

不仅可以,而且将参数传递给工厂方法是很常见的。请查看一些示例。通常,该参数是一个类型,告诉工厂要制造什么,但您也可以添加其他需要构建对象的信息。我认为你所做的是可以的。


6

我不明白为什么向您的工厂添加此参数会有错。但请注意,您不应该添加许多可能对工厂创建的所有对象都无用的参数。如果这样做,您将失去工厂的很多优势!


1
通过在参数中描述您想要的对象,这可以让工厂返回完全不同的对象(通过返回基类或接口)。 - Aardvark

5

工厂模式的思想是它可以为你提供类/接口的实例,因此传递参数是没有问题的。如果有问题的话,向new()方法传递参数也是不好的。


1

我同意Benoit的观点。可以将其想象成一个工厂,用于创建像SQL连接这样的东西,在这种情况下,有必要向工厂传递有关连接的信息。工厂将使用该信息来使用正确的服务器协议等。


1

当然,为什么不呢..!?

传递参数的好处在于它允许您隐藏具体对象的实现。例如,在您发布的代码中,您将参数传递给构造函数。但是,您可以更改实现方式,使其通过一个初始化方法进行传递。通过将参数传递给工厂方法,您可以将构建和初始化对象的本质从调用者中隐藏。


1

看一下Loki :: Factory,其中有一个非常相似的实现即将出现在Boost中。这是我经常使用不同版本的示例代码:

typedef Loki :: SingletonHolder<Loki :: Factory <Component,std :: string,Loki :: Typelist <const DataCollection&,Loki :: Typelist<Game*,Loki :: NullType>>>> ComponentFactory;

乍一看可能有些奇怪,但是让我解释一下这个东西及其真正的强大之处。基本上,我们创建一个持有工厂的单例,最外层参数是用于单例的,Component是我们的产品,std :: string是我们的创建ID类型,在此之后是用于创建组件所需的参数类型列表(这也可以使用宏定义来定义,以获得更简洁的语法)。在此行之后,只需执行以下操作:

ComponentFactory :: Instance()。CreateObject(“与具体类型相关的某些字符串”,anDataCollection,aGamePointer);

要创建对象,请注册一个使用ComponentFactory :: Instance()。Register()即可。在书籍《Modern C++ Design》中有关于详细信息的精彩章节。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接