【问题标题】:myth about factory pattern [closed]关于工厂模式的神话[关闭]
【发布时间】:2010-04-23 09:49:12
【问题描述】:

这困扰了我一段时间,我不知道这是否是一个神话。

似乎工厂模式可以减轻为类添加依赖项的痛苦。

例如,在一本书中,它有这样的东西

假设您有一个名为 Order 的类。最初它不依赖于任何东西。因此,您不必费心使用工厂来创建 Order 对象,而只需使用 plain new 来实例化对象。但是,您现在需要创建与客户关联的订单。您需要更改数百万个位置才能添加此额外参数。如果您为 Order 类定义了一个工厂,您就可以轻松满足新的要求。

这与向构造函数添加额外参数有何不同?我的意思是您仍然需要为工厂提供一个额外的参数,并且这也被数百万个地方使用,对吧?

【问题讨论】:

  • 那里有数百万(如果不是数十亿)书籍。并非所有这些都是正确的。不要因为有人写在一本书里就认为任何事情都是理所当然的。

标签: factory-pattern


【解决方案1】:

如果用户仅在订单创建时才知道,您可以实现由工厂调用的getCurrentUser() 函数。
如果这是可能的,那么工厂功能显然会胜出。如果没有,那就没有收获。

如果在过去,您不知道会有客户需要,您可能也不知道是否可以实现getCurrentUser() 函数。 工厂方法的回报率可能不是很好,但并不总是等于 0。

【讨论】:

  • 我认为这正是我想要的。
  • 完全正确。如果您需要新信息,您的工厂不会提供帮助。如果您拥有或可以获得所需的信息,那么工厂将帮助您避免到处进行更改。
【解决方案2】:

使用工厂的真正好处是它是一个外观,它隐藏了您如何创建一个满足订单角色的对象。更准确地说,Factory 知道您确实在制作 FooBarOrder,无需更改任何其他内容即可从始终制作 FooBarOrder 切换到有时制作 BarFooOrder。 (如果 Java 让您拦截 new 并改为创建子类,则不需要工厂。但它并不 - 相当合理,公平 - 所以你必须拥有它们。允许对类进行子类化的对象系统的类在这方面更加灵活。)

【讨论】:

  • 我也是这么理解工厂模式的。但是人们只是一直说它在添加依赖项的情况下也很有帮助,这是我不明白的。
  • 您最初有一个订单,由您的工厂创建,用于实现 IOrder。现在您想要一个 CustomerOrder。假设创建 CustomerOrder 所需的所有信息在创建 Order 所需的相同信息中可用,或者可以从该信息或静态确定,那么工厂现在可以返回 CustomerOrder 而无需对代码进行任何更改。如果工厂需要一个新的依赖项来帮助它获得创建 CustomerOrder 所需的 CustomerObject,那么该更改只需要发生在工厂,而不是发生在您创建订单的每个地方。
【解决方案3】:

不,因为工厂的依赖项应该通过工厂构造函数注入,并且您只是在一个地方构建工厂,而是将其作为依赖项传递给需要创建订单的所有内容。从工厂获得订单的东西仍然调用相同的方法,CreateOrder() 或其他方法,因此代码不变。

所有依赖项都应该连接在一个地方,composition root,并且应该是唯一需要更改的地方,以将新依赖项添加到工厂

【讨论】:

  • 我不太明白。假设我想创建一个 Order 对象,它依赖于一个客户对象,但客户仅在创建 Order 对象时才知道。我想如何在早期设置工厂?
  • 这不是向类添加依赖项,是吗?这是添加新信息。如果您需要新信息才能创建订单,那么您将需要在任何地方添加该信息。但是,如果您可以从现有信息中确定客户,如果说 Order 类可以访问 ICustomerResolver 实现,那么如果您使用了工厂,则可以将 ICustomerResolver 注入工厂并且您的 Order 创建代码将保持不变。如果没有工厂,您需要将 ICustomerResolver 注入到每个 Order 构造函数中。
  • 很抱歉,我认为新信息是依赖项。所有的设计都必须已经知道会有新的客户需求。这就是为什么我说这本书具有误导性。
【解决方案4】:

你告诉工厂新的依赖,让它为你添加。对工厂的方法调用应该保持不变。

【讨论】:

  • 如果我需要告诉工厂新的依赖关系,那么这与使用普通的旧“新”并添加额外参数有什么不同?新的依赖不是固定对象。
  • 你告诉工厂一次,而不是在代码中你调用工厂创建对象的任何地方。
【解决方案5】:

The factory pattern 可以减轻添加依赖的痛苦,因为一个工厂可能包含状态,实际上可以封装多个依赖(例如,不是提供三个依赖,而是调用一些对象的构造函数,您现在只提供一个工厂对象,其中工厂包含需要提供给构造函数的这三个对象。

举个例子,比较一下:

void DoIt(const DependencyA& a, const DependencyB& b) {
   // NOTE: "x" is a contrived additional variable that we add here to
   // justify why we didn't just pass DependencyC directly.
   int x = ComputeX(); 
   std::unique_ptr<DependencyC> dependency_c(new DependencyC(a, b, x));
   dependency_c->DoStuff();
}

还有:

void DoIt(const DependencyCFactory& factory) {
  int x = ComputeX();
  std::unique_ptr<DependencyC> dependency_c(factory->Create(x));
  dependency_c->DoStuff();
}

请注意,第二个版本需要对“DoIt”方法的依赖较少。这并不意味着整个程序不需要这些依赖项(实际上,程序在工厂的实现中仍然使用了DependencyA和DependencyB)。但是,通过这种方式构建它,可以将依赖项隔离到工厂代码,这使其他代码更简单,更容易更改 DependencyC 的依赖项(现在只有工厂本身需要更新,而不是每个实例化DependencyC 的地方),甚至可以具有一定的安全/保障优势(例如,如果DependencyADependencyB 是敏感的,例如数据库密码或API 密钥,则将它们的使用限制在工厂可以减少错误处理的机会,例如,与您在需要使用数据库或 API 的任何地方传递这些信息的情况相比)。

在书中给出的示例中,为Order 提供工厂的原因是它会减少直接使用构造函数的地方的数量;只有创建工厂的地方需要修改以将Customer 存储为工厂的附加字段;工厂的其他用途都不需要修改。相比之下,在不使用工厂的情况下,直接使用构造函数的情况比比皆是,而且它们中的每一个都必须更新才能以某种方式获得对 Customer 对象的访问权限。

【讨论】:

  • 感谢您的代码。但是,要创建 MyClassFactory2,我需要提供 p1 和 p2。如果 p1 和 p2 仅在我需要创建 MyClass 时才知道,那么与在构造函数中插入 extra 有何不同?
  • @leiz,如果参数是外部依赖项,那么提供该外部依赖项的任何东西也应该能够提供工厂对象。如果没有,那么你是对的,它没有帮助。
猜你喜欢
  • 1970-01-01
  • 2011-03-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多