【问题标题】:Motivation for Simple Factory and Factory Method Pattern简单工厂和工厂方法模式的动机
【发布时间】:2013-12-30 21:45:49
【问题描述】:

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

在简单工厂中,客户端使用带有工厂方法(可以是静态的)的单独类(Creator)来返回产品。

在工厂方法模式中,创建者和客户端是同一个东西,他们在同一个类中使用抽象方法来创建新产品,他们在同一个类中操作。当然,创造者(或客户)是抽象的,所以关于制作具体产品的决定被推迟到子类。

  1. 我的理解是否正确(例如,FMP 中的 Client 和 Creator 是否相同,我从未在 FMP 图中看到 Client)?

  2. 在工厂方法模式中,它缝合了create方法在Creator之外是不可重用的,所以它只能在制作一些新的Creator时重用?

  3. 在哪些情况下我可以二选一?

(P.S.请不要将此标记为重复,我想在本站明确说明)

【问题讨论】:

标签: java oop design-patterns factory factory-pattern


【解决方案1】:

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

Factory Method 是 方法 形式的 工厂(因此得名)。这并不违反打开/关闭原则,因为您通过扩展而不是通过修改代码来处理更改。

你的理解是正确的。 FMP 中的 Client 和 Creator/Factory 是相同的,因为 Factory(方法)是 Client 的一部分。

FMP 中的 create 方法确实是不可重用的。不过没关系,因为这不是尝试创建产品的应用程序范围的工厂,而是客户端在不使用 new 的情况下创建其依赖对象的一种方式。

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

【讨论】:

    【解决方案2】:

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

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

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

    工厂方法 - 您在超类中完成大部分工作,但在运行时才决定使用哪种对象。通常超类需要创建一些默认类型的工作者对象,但超类允许子类专门化工作者。工厂方法通常在 AbstractFactory 过度使用时使用,但一个缺点是它迫使您使用继承,这有其自身的一组维护和设计问题。这与 SimpleFactory 非常相似,只是您使用继承而不是鉴别器来获得不同的返回类型。

    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 对象的抽象工厂。 LinkedList 或 HashSet 等类有自己的 iterator() 实现(因此是具体工厂),它们返回实现迭代器接口的不同类(具体产品)

    一旦你完成 Head First Design Patterns 我推荐 Holub on Patterns 代码有点过时(前泛型),但你真的学到了很多关于如何使用多种模式在非平凡的代码示例中相互交互。本书只有2个代码示例,每个示例涵盖大约10个模式,每个示例需要100多页来逐步解释

    【讨论】:

    • 他询问了工厂方法与简单工厂的对比。抽象工厂不是一个简单的工厂。它用于创建对象系列。
    • @Rob 你是对的。我没有注意到“简单”这个词,因为那不是真正的模式。我已经更新了我的答案以包括它。谢谢!
    • 非常感谢本书的推荐 :)。关于我不同意的例子。首先我不认为 Simple Factory 只是一个方法,我认为它是一个带有这个工厂方法的对象。因此,我认为您可以使用此方法向客户端提供不同的“工厂”对象,因此它可以有不同的工厂(HF 设计模式第 119 页,您提供不同的工厂,但本书没有显示整个代码,您如何实现它们,所以这个例子很混乱)。
    • (在该示例中)也许您将简单工厂子类化,并为客户端对象提供不同的工厂(在构造函数中)-但我不知道这是否正确,或者成语是否变为那么抽象工厂呢?我同意 Simple Factory 不是模式,但你还没有回答我的问题。
    • 简单工厂并不是真正的模式。它只是将 PizzaStore 与 Pizza 创建代码分离。这样 PizaaStore 就变成了“关闭修改”。不过,简单工厂本身并不适合修改。蚂蚁从来都不是它的意图。
    【解决方案3】:

    简单工厂:

    定义:

    在不向客户端公开实例化逻辑的情况下创建对象。 通过通用接口引用新创建的对象

    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();
        }
    
    }
    

    【讨论】:

    • 这个例子非常简洁。谢谢 。但是你也可以在这里展示 AbstractFactory 吗?
    【解决方案4】:

    您会看到两种工厂方法的示例,因为它们适用于两种完全不同的情况。

    第一种类型——这似乎与您被引导称为简单工厂的方式相吻合,当您拥有一个相对复杂的对象时,由于它的复杂性而可能难以创建。

    这里的经典示例是 Pizza 类,它有一个 PizzaConstructor 类(或类似名称),其中构建 Pizza 对象所需的许多聪明才智被编码到 Constructor 中。

    here 有一个很好的讨论,但重点是这种形式将如何构造Pizza 的聪明才智放在Factory 中,而不是陷入Pizza 类.

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

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

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

    【讨论】:

    • 1.好吧,除了有一个难以创建的对象之外,还有其他情况让我想使用简单工厂,或者不? 2.所以你想说当我们希望我们框架的用户(?)提供具体的构造机制时,我们使用工厂方法?
    • 请澄清(扩展)您的代码以使其完整,例如。带有调用代码,没有缩写。
    • 这更像是建造者模式而不是工厂模式。
    • @dkatzel 任何负责创建其他对象的程序元素都称为工厂 ...
    【解决方案5】:

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

    这两者的基本区别是一个提供了具体的创建者(Simple Factory),而另一个(工厂方法)支持通过继承解耦创建者,从而可以支持多个创建者。一个更清楚的例子 -

    1. 工厂方法 - 考虑创建具有不同类型的 Button - Toggle、ImageButton 等。您更有可能拥有不同的 Button 创建者。例如。基于不同的操作系统,WindowsButtonFactory, OSXButtonFactory 等。工厂方法适用于这种情况,抽象类ButtonFactory 由WindowsButtonFactory, OSXButtonFactory 实现(未来还有更多)。
    2. 简单工厂 - 考虑根据流派创建 Book - Fiction, NonFiction 等。您通常会认为具体工厂在这里就足够了。

    【讨论】:

      【解决方案6】:

      首先,我不认为“工厂方法模式”是一个独立的模式(在我阅读了你在问题中所说的维基百科之前,我从来没有考虑过这种模式)。我认为它更像是工厂模式 + 战略模式的混合体。

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

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

      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() {
      
          }
      }
      
      • 您还可以有一个工厂类,它只负责创建一种类型的对象。当创建逻辑非常复杂时,您可以为每个对象类型创建一个工厂类。

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

      制造工厂是为了隐藏一些对象的实现逻辑。您将策略模式添加到工厂以向客户端隐藏这些细节。

      【讨论】:

      • 嗯,工厂方法模式是官方的 GOF 模式。在其中,您的 Car 类中有一个工厂方法,该方法是抽象的(或使用默认行为实现),您必须将 Car 子类化并在子类中实现此方法以提供特定行为(工厂方法也可以带参数)。不知道在这种情况下是否适用?我希望我的理解是正确的?但是我没有看到这样做的真正好处(至少不是简单的工厂,这是我最初的问题,它们之间的好处和区别是什么?),也许我的想法不是面向对象的。
      • audioSystemFactory 是您所说的工厂方法。但我根本不认为它是一个独立的模式。实际上这里涉及到两种模式:工厂模式和策略模式。 “工厂方法”的逻辑是一种简单的策略,根据上下文表现不同。我认为工厂模式是解决当客户必须对操作对象的结构/行为了解太多时出现的问题的解决方案。您所说的这种工厂方法是一种简单的策略。这是一个完全成熟的工厂类可能包含的简单行为。
      • 也许可以尝试阅读这篇文章来了解工厂的作用:amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/…
      • 你必须知道工厂模式不存在。书籍(例如 Head First Design Patterns)称其为成语。 这个成语到底代表什么,以及它与工厂方法模式(在 GOF 的设计模式中称为)的关系是我最大的谜团?你说工厂方法模式是这个成语和策略模式的结合?这是可能的,但我不相信。 (我熟悉 Java)我的另一个问题是什么时候使用哪个?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多