简单工厂和工厂方法模式的动机

我知道有很多关于不同工厂模式的差异的问题,但答案是如此不同和令人困惑。 我读过的书籍使用不清楚和(简化)简化的例子。 即使在阅读了维基百科的解释之后,我也有很多问题,以及关于它们的大量在线解释,包括所有这些网站上的解释。 我正在阅读的这本书是Head First Design Patterns。

在Simple Factory中,客户端使用单独的类(Creator)和工厂方法(可以是静态的)来返回Products。

在工厂方法模式中,创建者和客户端是相同的东西,他们在同一个类中使用抽象方法来创建新的产品,它们在同一个类中运行。 当然,造物主(或客户)是抽象的,因此关于制作混凝土产品的决定被推迟到子类。

  1. 我的理解是否正确(例如,FMP中的客户端和创建者是相同的,我从未在FMP图中看到客户端)?

  2. 在Factory Method Pattern中,它表明create方法不能在Creator之外重用,所以它只能在创建一个新的Creator时重用?

  3. 有什么情况我可以选择一个而不是另一个?

(PS请不要将此标记为重复,我想在此网站上明确这一点)

Simple Factory是一个class级forms的工厂 。 因为它不能解决优雅问题,因为对于Product的每个新子类,您必须在create()方法中编辑switch语句。 这违反了开/关原则 。 使简单工厂变得有用的一种潜在方法是使用如下所示的类注册: http : //www.oodesign.com/factory-pattern.html

Factory Method是一个方法forms的工厂 (因此名称)。 这不违反开放/关闭原则,因为您通过扩展而不是通过修改代码处理更改。

你的理解是正确的。 FMP中的客户端和创建者/工厂是相同的,因为工厂(方法)是客户端的一部分。

确实,FMP中的create方法不可重用。 这是可以的,因为这不是尝试创建应用程序范围的产品工厂,而是客户端创建他的依赖对象而不使用新的方法

我不能回答你的第三个问题,因为我认为这是基于偏好。

简单的工厂 – 这不是官方的GoF模式,事实上我不知道你在谈论什么,直到我拿出我的Head First Design Pattern Book。 Simple Factory只是一种可以返回不同硬编码子类型的方法。

public Pizza createPizza(String type){ if(type.equals("cheese")){ return new CheesePizza(); }else if (type.equals("pepperoni")){ return new PepperoniPizza(); } ... } 

这段代码的问题是你只能使用硬编码类型。 如果要更改方法的工作方式或返回的类型,则必须修改代码并重新编译。 添加新类型将非常困难。

工厂方法 – 您在超类中完成大部分工作,但是决定在运行之前决定使用哪种对象。 通常,超类需要创建某种默认类型的工作对象,但超类允许子类专门化工作者。 工厂方法通常在AbstractFactory过度使用时使用,但一个缺点是它会强制您使用具有自己的一组维护和设计问题的inheritance。 这与SimpleFactory非常相似,除了使用inheritance而不是鉴别器来获取不同的返回类型。

 public void foo(){ Bar bar = createBar(); //do stuff with bar } //method is protected so subclass can override it to return something else protected Bar createBar(){ return new DefaultBar(); } 

AbstractFactory – 创建对象只知道它们实现的接口而不是实际的类。 AbstractFactories使代码可以轻松地在不同的系统中工作,因为您不需要知道所使用的具体工厂或具体产品。

例如, Collection.iterator()Iterator对象的抽象工厂。 像LinkedListHashSet这样的类有自己的iterator() (因此是具体工厂),它们返回实现迭代器接口的不同类(具体产品)

一旦你完成Head First Design Patterns我推荐Holub on Patterns ,代码有点过时(pre-generics),但你真的学到了很多关于多个模式在非平凡代码示例中如何相互作用的知识。 这本书只有2个代码样本,每个代码样本覆盖大约10个模式,每个样本分别用100多个页面来解释

您将看到两种工厂方法样式的示例,因为有两种完全不同的情况,它们是合适的。

第一种类型 – 它似乎与你被称为简单工厂的东西相吻合,当你有一个相对复杂的对象时,由于它的复杂性而难以创建。

这里的经典示例是Pizza类,它有一个PizzaConstructor类(或类似名称),其中构建Pizza对象所需的大部分PizzaConstructor被编码到Constructor

这里有一个很好的讨论,但重点是这种forms让人们如何在Factory 建造 Pizza ,而不是在Pizza课上捣乱。

使用这种技术,您可以使构造函数代码比通常更清晰。

 Pizza pizza = PizzaFactory.addTopping(Cheese) .addTopping(Anchovies) .thickCrust() .stuffedCrust(Gruyere) .stoneBaked() .buildPizza(); 

使用工厂的第二种情况是,当您需要您的类能够实际制作所提供类型的对象时。 如果没有用户提供工厂机制供您使用,这很困难。 然后,用户提供某种工厂机制,可能是工厂对象,也可以扩展抽象类并提供具体的构造方法。

简单工厂:

定义:

创建对象而不将实例化逻辑暴露给客户端。 通过通用接口引用新创建的对象

 public interface PaymentMethod { public void makePayment(); } class CreditCard implements PaymentMethod { public void makePayment() { System.out.println("Payment through credit card..."); } } class NetBanking implements PaymentMethod { public void makePayment() { System.out.println("Payment through net banking..."); } } public class PaymentMethodFactory { public static PaymentMethod getPaymentMethod(String method) { if ("creditcard".equalsIgnoreCase(method)) { return new CreditCard(); } else if ("netbanking".equalsIgnoreCase(method)) { return new NetBanking(); } else { throw new IllegalArgumentException("Payment method not supported!"); } } } public class SimpleFactoryTest { public static void main(String[] args) { PaymentMethodFactory factory = new PaymentMethodFactory(); PaymentMethod paymentMethod = factory.getPaymentMethod("creditcard"); paymentMethod.makePayment(); } } 

工厂方法:

定义:

定义用于创建对象的接口,但让子类决定实例化哪个类通过公共接口引用新创建的对象。

 public interface PaymentMethod { public void makePayment(); } class CreditCard implements PaymentMethod { public void makePayment() { System.out.println("Payment through credit card..."); } } class NetBanking implements PaymentMethod { public void makePayment() { System.out.println("Payment through net banking..."); } } public interface IPaymentMethodFactory { public PaymentMethod getPaymentMethod(); } class CreditCardFactory implements IPaymentMethodFactory { public PaymentMethod getPaymentMethod() { return new CreditCard(); } } class NetBankingFactory implements IPaymentMethodFactory { public PaymentMethod getPaymentMethod() { return new NetBanking(); } } public class FactoryMethodTest { public static void main(String[] args) { IPaymentMethodFactory factory = new CreditCardFactory(); PaymentMethod paymentMethod = factory.getPaymentMethod(); paymentMethod.makePayment(); } } 

首先,我没有看到“工厂方法模式”作为一个独立的模式(从来没有像这个模式的头衔,直到我在你的问题中所说的读过维基百科)。 我把它看作是工厂模式+战略模式的混合体。

您可以将对象工厂想象为一个简单的对象创建者。
当策略模式在对象工厂内部发挥作用时,您开始添加关于对象创建的更多逻辑,以将其从客户端隐藏(客户端不应该知道如何创建对象,将此责任留给工厂)。

工厂可以是多种类型(工厂创建的决定取决于许多因素):
– 在考虑对象关联时,对象可以由多个对象组成。 这里根对象可以有一个工厂方法来创建需求对象。 此工厂方法负责检查新添加的对象是否使根保持有效状态。 所以这里的战略模式(你称之为工厂方法模式)可能会发挥作用:

 class Car { public $carType; public $audioSystem; public function __construct($carType) { $this->carType = $carType; $this->audioSystemFactory(); } public function audioSystemFactory() { if ($this->carType == 'hipster') { $this->audioSystem = new VynilPlayer(); } else { $this->audioSystem = new Mp3Player(); } } public function startMusic($albumName) { $this->audioSystem->playSongs($albumName); } } class VynilPlayer { public $vynilAlbums = array('MJ TOP HITS', 'JUSTIN BIEBER TOP HITS'); public function playSongs($albumName) { $this->loadVynil(); $this->startPlayer(); } public function loadVynil() { } public function startPlayer() { } } class Mp3Player { public $dvdAlbums = array('MJ TOP HITS', 'JUSTIN BIEBER TOP HITS'); public function playSongs($albumName) { $this->loadDVD(); $this->startPlayer(); } public function loadDVD() { } public function startPlayer() { } } 
  • 您还可以拥有一个工厂类,它只负责创建一种类型的对象。 当创建逻辑非常复杂时,您为每个对象类型创建一个工厂类。

  • 即使像类构造函数这样的原始forms的实例也可以被视为对象工厂。

工厂可以隐藏某些对象的实现逻辑。 您将策略模式添加到工厂以从客户端隐藏这些详细信息。

这是一个我能想到的简单指南 –

这两者之间的基本区别是一个提供了一个具体的创建者(简单工厂),而另一个(工厂方法)支持创建者通过inheritance解耦,以便可以支持多个创建者。 一个让它更清晰的例子 –

  1. 工厂方法 – 考虑创建一个具有不同类型的ButtonToggleImageButton等。你很可能会有不同的Button创建者。 例如。 基于不同的操作系统, WindowsButtonFactory, OSXButtonFactory等。在这种情况下,工厂方法适用于由WindowsButtonFactory, OSXButtonFactory (以及将来还有更多)实现的抽象类ButtonFactory
  2. 简单的工厂 – 考虑根据类型创建BookFiction, NonFiction等。通常你会认为具体的工厂应该足够。