如何在工厂方法模式和抽象工厂模式之间进行选择

Posted

技术标签:

【中文标题】如何在工厂方法模式和抽象工厂模式之间进行选择【英文标题】:How to choose between Factory method pattern and Abstract factory pattern 【发布时间】:2012-02-15 16:36:25 【问题描述】:

我知道以前有人问过类似的问题。在过去的几天里,我已经阅读了很多关于此的内容,我想我现在可以理解设计和代码流方面的差异。困扰我的是,这两种模式似乎都可以解决相同的一组问题,而没有真正的理由来选择一个或另一个。 当我试图自己解决这个问题时,我尝试实现一个小例子(从我在“Head First:Design patterns”一书中找到的那个开始)。 在此示例中,我尝试两次解决相同的问题:一次仅使用“工厂方法模式”,另一次使用“抽象工厂模式”。我将向您展示代码,然后我将制作一些 cmets 和问题。

常用接口和类

public interface IDough  
public interface ISauce  
public class NYDough : IDough  
public class NYSauce : ISauce  
public class KNDough : IDough  
public class KNSauce : ISauce  

纯工厂方法模式

// pure Factory method pattern
public abstract class Pizza

    protected IDough Dough  get; set; 
    protected ISauce Sauce  get; set; 
    protected abstract IDough CreateDough();
    protected abstract ISauce CreateSauce();
    public void Prepare()
    
        Dough = CreateDough();
        Sauce = CreateSauce();
        // do stuff with Dough and Sauce
    
    public void Bake()  
    public void Cut()  
    public void Box()  


public class NYCheesePizza : Pizza

    protected override IDough CreateDough()
    
        return new NYDough();
    

    protected override ISauce CreateSauce()
    
        return new NYSauce();
    


public class KNCheesePizza : Pizza

    protected override IDough CreateDough()
    
        return new KNDough();
    

    protected override ISauce CreateSauce()
    
        return new KNSauce();
    



public abstract class PizzaStore

    public void OrderPizza(string type)
    
        Pizza pizza = CreatePizza(type);
        pizza.Prepare();
        pizza.Bake();
        pizza.Cut();
        pizza.Box();
    
    public abstract Pizza CreatePizza(string type);


public class NYPizzaStore : PizzaStore

    public override Pizza CreatePizza(string type)
    
        switch (type)
        
            case "cheese":
                return new NYCheesePizza();
            default:
                return null;
        
    


public class KNPizzaStore : PizzaStore


    public override Pizza CreatePizza(string type)
    
        switch (type)
        
            case "cheese":
                return new KNCheesePizza();
            default:
                return null;
        
    

纯抽象工厂模式

public interface IIngredientFactory

    IDough createDough();
    ISauce createSauce();


public class NYIngredientFactory : IIngredientFactory

    public IDough createDough()
    
        return new NYDough();
    

    public ISauce createSauce()
    
        return new NYSauce();
    


public class KNIngredientFactory : IIngredientFactory

    public IDough createDough()
    
        return new KNDough();
    

    public ISauce createSauce()
    
        return new KNSauce();
    


public class Pizza

    IDough Dough  get; set; 
    ISauce Sauce  get; set; 
    IIngredientFactory IngredientFactory  get; set; 

    public Pizza(IIngredientFactory ingredientFactory)
    
        IngredientFactory = ingredientFactory;
    

    public void Prepare()
    
        Dough = IngredientFactory.createDough();
        Sauce = IngredientFactory.createSauce();
    
    public void Bake()  
    public void Cut()  
    public void Box()  


public interface IPizzaFactory

    Pizza CreatePizza(string type);


public class NYPizzaFactory : IPizzaFactory

    public Pizza CreatePizza(string type)
    
        switch (type)
        
            case "cheese":
                return new Pizza(new NYIngredientFactory());
            default:
                return null;
        
    


public class KNPizzaFactory : IPizzaFactory

    public Pizza CreatePizza(string type)
    
        switch (type)
        
            case "cheese":
                return new Pizza(new KNIngredientFactory());
            default:
                return null;
        
    


public class PizzaStore

    IPizzaFactory PizzaFactory  get; set; 

    public PizzaStore(IPizzaFactory pizzaFactory)
    
        PizzaFactory = pizzaFactory;
    

    public void OrderPizza(string type)
    
        Pizza pizza = PizzaFactory.CreatePizza(type);
        pizza.Prepare();
        pizza.Bake();
        pizza.Cut();
        pizza.Box();
    

如果我使用了模式定义,我会为PizzaStore 选择“工厂方法模式”(因为它只构建一种类型的对象,Pizza),为IngredientFactory 选择“抽象工厂模式”。无论如何,另一个设计原则指出你应该“更喜欢组合而不是继承”,这表明我应该始终使用“抽象工厂模式”。

我的问题是:首先我应该选择“工厂方法模式”的原因是什么?

编辑

让我们看一下第一个实现,它使用工厂方法模式。 Jesse van Assen 建议这是模板方法模式而不是工厂方法模式。我不相信这是正确的。 我们可以将第一个实现分为两部分:第一个处理Pizza,第二个处理PizzaStore

1) 在第一部分中,Pizza 是依赖于某种具体的 Dough and Sauce 的客户端。为了将 Pizza 与我使用的具体对象分离,在 Pizza 类中,仅引用接口(@98​​7654331@ 和 ISauce),我让 Pizza 的子类决定哪个具体的 Dough 和 @987654335 @ 选择。对我来说,这完全符合工厂方法模式的定义:

定义一个用于创建对象的接口,但让子类决定实例化哪个类。 Factory 方法允许类将实例化推迟到子类。

2) 第二部分中的PizzaStore 是客户端,它依赖于具体的Pizza。我应用了上面讨论的相同原则。

所以,为了更好地表达(我希望)我没有真正理解的是为什么这么说:

工厂方法模式负责创建属于一个系列的产品,而抽象工厂模式处理多个产品系列。

正如您从我的示例中看到的(只要它们是正确的 :-))您可以使用两种模式来实现相同的东西。

【问题讨论】:

Differences between Abstract Factory Pattern and Factory Method 【参考方案1】:

首先,引用 GoF 设计模式书的两句话:

“抽象工厂通常用工厂方法实现。”

“工厂方法通常被模板方法调用。”

所以这不是在工厂方法和抽象工厂之间进行选择的问题,因为后者可以(并且通常是)由前者实现。

抽象工厂的概念(正如 Amir 所暗示的那样)是将几个始终在一起的具体类的创建组合在一起。在您的示例中,它们应该是 NY 的食品成分品种,而不是 KN 的品种。

但是,如果您想允许混搭(KN 面团和 NY 酱的披萨有什么问题?)那么抽象工厂不是您的答案。在这种情况下,每个 Pizza 子类都应该决定它希望创建的具体类。

如果您不想允许这些类型的混合,您应该使用抽象工厂。

【讨论】:

抱歉,我可能完全不懂你的意思,但我不明白抽象工厂如何不允许你所谓的“混合与匹配”。您始终可以定义另一个从 IIngredientFactory 继承并返回您想要混合的具体类型的工厂。 嘿,我自己还远不是设计模式专家 :) 从技术上讲,你可以做任何事情,但它不会被视为抽象工厂。抽象工厂的思想是将具体对象分组到家庭。如果您确实想要混合,我根本不会使用子类化,因为它可能导致类膨胀。我可能会使用某种构建器 - Pizza 类将使用某种标识符指定它想要的组件类型(NY 面团、KN 酱),并且构建器将负责创建具体对象。【参考方案2】:

如果你想要几个相关的工厂方法来做同样的决定,那么最好在抽象工厂中进行分组。

我会说你的第一个实现不是工厂方法。工厂方法不是抽象的,它们有参数决定根据它们实例化什么。

【讨论】:

感谢您花时间回答。我认为,在工厂方法中,方法应该是抽象的,因为您希望在子类中实现它们。这是模式的要求。另一方面,关于参数,我不完全确定这是一个要求。 您使用的模式是模板方法,而不是工厂方法。 @JessevanAssen :谢谢,您可能启发了我,现在甚至 Amir 的回答对我来说也更有意义。我不知道模板方法模式,我将进一步研究它,稍后我会发布一些内容。 mm 我还不相信,我做了一些编辑以更好地解释我的意思。我希望您有时间阅读并分享您的想法。谢谢。

以上是关于如何在工厂方法模式和抽象工厂模式之间进行选择的主要内容,如果未能解决你的问题,请参考以下文章

Java描述设计模式(04):抽象工厂模式

Java设计模式(创建型:工厂方法模式+抽象工厂模式)

案例分析:设计模式与代码的结构特性

抽象工厂模式

抽象工厂模式和原型模式之间的区别是什么?

设计模式-抽象工厂