这可以被视为工厂方法模式(或等效模式)吗?

Can this be considered a Factory Method pattern (or an equivalent)?

在我正在学习的课程中,PizzaStore 使用 SimplePizzaFactory class 来处理具体的比萨饼实例化,描述如下:

在课程中,通过介绍需要为 PizzaStore 提供额外级别的特异性以及提供相同类型的比萨饼(Viggie、Cheese 等)的能力来描述工厂方法模式。但是在纽约风格和芝加哥风格中,所以我们有一组新的子classes (NYStyleViggiePizza, NYStyleCheesePizza, .. ChicagoStyleViggiePizza, ChicagoStyleCheesePizza, ..)

老师介绍的解决方案是使用工厂方法模式如下:

(UML)

代码在 python 中重写:

# Pizzas Subclasses are defined elsewhere
from abc import ABC, abstractmethod

class PizzaStore(ABC):
    @abstractmethod
    def create_pizza(self):
        pass

    def order_pizza(self,type_of_pizza):
        type_of_pizza = type_of_pizza.lower()
        pizza = self.create_pizza(type_of_pizza)
        pizza.prepare()
        pizza.bake()
        pizza.box()
        return pizza


class NYPizzaStore(PizzaStore):
    def create_pizza(self, type_of_pizza):
        if type_of_pizza == "cheese":
            pizza = NYStyleCheesePizza()
        
        elif type_of_pizza == "pepperoni":
            pizza = NYStylePepperoniPizza()

        elif type_of_pizza == "clam":
            pizza = NYStyleClamPizza()

        elif type_of_pizza == "viggie":
            pizza = NYStyleViggiePizza()
        else:
            raise Exception("You need to specify a type of NY pizza.")   
        return pizza


class ChicagoPizzaStore(PizzaStore):
    def create_pizza(self,type_of_pizza):
        if type_of_pizza == "cheese":
            pizza = ChicagoStyleCheesePizza()
        elif type_of_pizza == "pepperoni":
            pizza = ChicagoStylePepperoniPizza()

        elif type_of_pizza == "clam":
            pizza = ChicagoStyleClamPizza()

        elif type_of_pizza == "viggie":
            pizza = ChicagoStyleViggiePizza()
        else:
            raise Exception("You need to specify a type of NY pizza.")
        
        return pizza


# ===== Driver Code =====
# NY store
ny_pizza_store = NYPizzaStore()
ny_pizza_store.order_pizza("Cheese")
ny_pizza_store.order_pizza("Pepperoni")


print()


# Chicago store
chicago_pizza_store = ChicagoPizzaStore()
chicago_pizza_store.order_pizza("Cheese")
chicago_pizza_store.order_pizza("Pepperoni")

在进入工厂方法之前,我尝试了以下设计,在那里我保持 PizzaStore 不变,并将 SimpleFactoryPizza 替换为两个新的 classes:NYPizzaFactoryChicagoPizzaFactory

代码在 python 中重写:

class NYPizzaFactory():
    def create_pizza(self,type_of_pizza):
        if type_of_pizza == "cheese":
            pizza = NYStyleCheesePizza()
        elif type_of_pizza == "pepperoni":
            pizza = NYStylePepperoniPizza()

        elif type_of_pizza == "clam":
            pizza = NYStyleClamPizza()

        elif type_of_pizza == "viggie":
            pizza = NYStyleViggiePizza()
        else:
            raise Exception("You need to specify a type of NY pizza.")
        
        return pizza

class ChicagoPizzaFactory():
    def create_pizza(self,type_of_pizza):
        if type_of_pizza == "cheese":
            pizza = ChicagoStyleCheesePizza()
        elif type_of_pizza == "pepperoni":
            pizza = ChicagoStylePepperoniPizza()

        elif type_of_pizza == "clam":
            pizza = ChicagoStyleClamPizza()

        elif type_of_pizza == "viggie":
            pizza = ChicagoStyleViggiePizza()
        else:
            raise Exception("You need to specify a type of NY pizza.")
        
        return pizza

# PizzaStore is the same as before

class PizzaStore:
    def __init__(self, pizza_factory_obj):
        self.pizza_factory_obj = pizza_factory_obj

    def order_pizza(self,type_of_pizza):
        type_of_pizza = type_of_pizza.lower() 
        pizza = self.pizza_factory_obj.create_pizza(type_of_pizza)
        pizza.prepare()
        pizza.bake()
        pizza.box()
        return pizza

# ===== Driver Code ======
# NY Store
ny_pizza_factory = NYPizzaFactory()
ny_pizza_store = PizzaStore(ny_pizza_factory)
ny_pizza_store.order_pizza("Cheese")
print()
ny_pizza_store.order_pizza("Pepperoni")
print()

# Chicago Store
chicago_pizza_factory = ChicagoPizzaFactory()
chicago_pizza_store = PizzaStore(chicago_pizza_factory)
chicago_pizza_store.order_pizza("Cheese")
print()
chicago_pizza_store.order_pizza("Pepperoni")

我知道工厂方法允许 class 将实例化延迟到其子classes,其中这些子classes 将包括该“工厂方法”的实现。

问题一:

问题二:

工厂方法结构由以下 UML 概括:(来自课程 material)

在“设计模式:可重用面向对象软件的元素”一书中,工厂方法模式的结构是通过以下 UML 描述的:

您后面描述的模式是抽象工厂模式;有几个工厂实现继承自同一个 abstract 工厂。这当然是回答问题 #1 的工厂模式的变体。

对于问题 #2,聚合与依赖关系实际上是一种风格问题。 GoF 对依赖的使用(逻辑上)弱于聚合(即 Factory depends on Product 是一个比 Factory aggregates Product 更弱的概念)。两者都传达了信息——即两者都有效。

我个人更喜欢依赖关系,因为我认为工厂实际上确实聚合了产品。对于 聚合——想想汽车聚合车轮。这实际上不是 Factory 和 Product 之间关系的平行概念。一旦创建了产品,工厂就不再与它有任何关系。继续这个例子,一旦汽车工厂制造了汽车,汽车就离开了工厂,并且永远不会 returns —— 因此很难说汽车在某种程度上是制造它的工厂的总和部分。不过,这是我的看法。

我认为课程 material 图表中的聚合是错误的。 Client 将聚合(抽象)工厂,而不是相反,类似地,工厂将聚合产品。我也不完全确定为什么 Client 不会直接引用 Product,因为工厂的重点是抽象对象创建,不使用。

Q1:你的实现是工厂吗?

工厂方法模式意图

define an interface for creating an object, but let the subclass decide which class to instantiate - (GoF, page 107).

您的设计和 re-implementation 正是这样做的并且是工厂。

更详细的论证

在您的 re-written 解决方案中,根据您的图表,PizzaStore 是某种上下文,它可以使用 NYPizzaFactoryChicagoPizzaFactory 或两者。您的代码比 UML 更清晰,因为您在构造时将工厂注入商店。

您的工厂似乎都是生产产品实例的具体创造者Pizza。每个具体的创建者都会创建一组不同的具体比萨饼。单独来看,您的每个 XxxPizzaFactory 似乎都对应于一个具体的工厂,FactoryMethod() 被称为 create_pizza()

图表和代码中唯一缺少的是工厂可互换的保证,方法是让它们继承更通用的 PizzaFactory。对你来说幸运的是,Python 的动态类型可以应对缺少相同基数 class 的情况。但出于维护目的,在 UML 和 Python 中使用显式子 classing.

更好地构造 classes

工厂还是抽象工厂?

您的每个具体工厂都可以创建不同类型的 Pizza 这一事实是称为“参数化工厂方法[=93=”的模式的变体]”GoF,第 110 页)。所以它绝对是工厂模式,只是 create_pizza() 接受一个参数来指定实例化哪个具体的披萨。

这不是抽象工厂,因为抽象工厂旨在创建相关或依赖产品的系列。家族中的每一种产品都有其特定的工厂方法。如果您有多个创建方法,例如 create_pizza(), create_dessert(), create_wine(),这里就是这种情况。在这里,情况并非如此,因为每个工厂只生产一种产品。

Q2:聚合还是依赖?

首先,GoF 不使用 UML(参见 GoF,第 363 页)。写这本书的时候UML还没有正式出版:

  • GoF 使用 OMT 而不是 UML class 图
  • GoF 混合使用 Booch and Objectory 的交互图。

有趣的是,OMT、Booch 和 Objectory 是三个主要的 OO 符号,它们被合并以创建 UML。

从 UML 的角度来看,

  • ConcreteCreatorConcreteProduct之间的关系是«create» dependency。其实CreatorProduct之间也应该有«create»依赖。

  • FactoryProduct 之间不应存在聚合或关联(除非任一产品会跟踪创建它的工厂或工厂会保留它创建的所有产品的列表)。

  • 有一个问题关于side of the agregation: you could use an aggregation between Client and Factory, but with the diamond on the client side. Nevertheless, while it is not fundamentally wrong a simple association更能代表两个class之间的关系。

附加信息:

  • Why a simple association would be more suitable than the aggregation in GoF:这是对一个不相关问题的回答的第二部分(在其评论中,我列出了 GoF 中 incont 的使用,它在两种符号之间交替)
  • This answer and this answer 到不相关的问题解释了为什么在方法中创建、返回或临时使用 class 的事实不足以建立关联(并且因为聚合比简单关联强, 对于聚合来说就更不够了)。

PS: 我用 GoF 参考《设计模式:可重用元素Object-Oriented软件