【问题标题】:Can this be considered a Factory Method pattern (or an equivalent)?这可以被视为工厂方法模式(或等效模式)吗?
【发布时间】:2020-12-27 08:50:50
【问题描述】:

在我正在学习的一门课程中,PizzaStore 使用了处理具体披萨实例化的 SimplePizzaFactory 类,描述如下:

在课程中,工厂方法模式的介绍通过介绍需要为 PizzaStore 提供额外级别的特殊性以及提供相同类型的 Pizzas(Viggie、Cheese 等)的能力来描述,但在纽约风格和芝加哥风格,所以我们有一组新的子类(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 替换为两个新类: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")

我了解工厂方法允许类将实例化推迟到其子类,这些子类将包含该“工厂方法”的实现。

问题 1:

  • 根据定义,我的解决方案不被视为工厂模式吗?与所代表的工厂方法相比,我尝试的方法有什么区别和缺点?

问题 2:

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

在“Design Patterns: Elements of Reusable Object-Oriented Software”一书中,工厂方法模式的结构是通过这个 UML 描述的:

  • 工厂和产品之间的箭头代表课程资料中的“聚合”,而书中的UML图代表“依赖”(我认为),哪一个代表正确的关系?为什么?

【问题讨论】:

  • 你的方法不应该是静态的吗?
  • 如果你的意思是第二个实现,在工厂类中使 create_pizza() 静态,那么我认为你是正确的,老实说,我只是跟随课程的 java 实现并比较多种方法不同来源查看差异。
  • 也许您可以将问题限制为一个问题,而不是当前的四个问题。
  • @khelwood 我以为他们的目标是相同的,但我会这样做。
  • 起初工厂在 Python 中比在 Java 中少得多。然后是__new__,它实际上可以在创建对象时返回不同类的对象。然后工厂没有状态,在使用该方法之前不需要创建工厂实例。它可以是静态的。

标签: python uml aggregation factory-pattern


【解决方案1】:

Q1:你的实现是工厂吗?

工厂方法模式的意图

定义一个用于创建对象的接口,但让子类 决定要实例化哪个类 -(GoF,第 107 页)。

您的设计和重新实现正是这样做的,并且是工厂。

更详细的论据

在您重新编写的解决方案中,根据您的图表,PizzaStore 是某种上下文,可以使用NYPizzaFactoryChicagoPizzaFactory 或两者。您的代码比 UML 更清晰,因为您在构建时将工厂注入到存储中。

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

图表和代码中唯一缺少的就是保证工厂是可互换的,让它们继承自更通用的PizzaFactory。幸运的是,Python 的动态类型可以应对相同基类的缺失。但出于维护目的,使用显式子类化更好地构建 UML 和 Python 中的类。

工厂还是抽象工厂?

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

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

Q2:聚合还是依赖?

首先,GoF 不使用 UML(参见 GoF,第 363 页)。本书写作时,UML 尚未正式出版:

  • GoF 对类图使用 OMT 而不是 UML
  • GoF 将BoochObjectory 混合用于交互图。

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

从 UML 的角度来看,

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

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

  • side of the agregation 存在一个问题:您可以使用 ClientFactory 之间的聚合,但在客户端使用菱形。尽管如此,虽然从根本上来说并没有错,但simple association 会更好地代表两个类之间的关系。

附加信息:

PS:我使用 GoF 来指代“设计模式:可重用的面向对象软件的元素”

【讨论】:

  • 像往常一样,一个很好的详细答案
  • 你应该指出OP的最后一张图片不是UML。
  • @qwerty_so 我做了:最后一张图片是 OP 解释的 GoF 书的图表,我回答 Q2 的第一件事是 GoF 不使用 UML 而是 OMT(顺便说一句有助于普及这种用图表中的聚合表示对象组合的可讨论实践,OMT 的发明者 Rumbaugh 本人称之为 modelling placebo) ;-)
  • 啊,我明白了。我不知道这种方言。但安慰剂是一个不错的 :-)
  • @qwerty_so 是的,这句话真的很到位! OMT几乎是考古学。它于 1991 年出版(比 1992 年 Jacobson 的 OOSE 和 Booch 早了一年)。 GoF 于 1995 年出版(基于 1991 年 Gamma 博士论文的工作)。那时,Booch 和 Rumbaugh 已经致力于统一他们的符号,他们在 1995 年刚被 Jacobson 加入。 UML 在 1997 年被 OMG 采用。GoF 可以用 UML 版本更新这本书(就像 Rumbaugh 用他自己的书所做的那样)但我认为 Java 的出现以及接口实现与继承的争论使关于模型的讨论变得复杂跨度>
【解决方案2】:

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

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

我个人更喜欢依赖,因为我认为工厂实际上确实聚合了产品。对于聚合——想一想 Car 聚合 Wheels。这实际上并不是工厂和产品之间关系的平行概念。一旦创建了产品,工厂就与它无关了。继续这个例子,一旦汽车工厂制造了汽车,汽车就会离开工厂并且永远不会回来——所以很难说汽车是制造它的工厂的总和。不过,这是我的看法。

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

【讨论】:

  • 有趣的论点。尽管如此,它仍然是一种工厂模式,因为混凝土工厂只生产一种产品。更准确地说,它是 GoF 第 110 页中解释的“参数化工厂方法”,这是一种允许工厂根据工厂方法的参数实例化不同种类的具体产品的变体。抽象工厂意味着一系列相互关联或相互依赖的产品,即几种抽象产品,以及每种抽象工厂方法。
猜你喜欢
  • 2023-03-23
  • 1970-01-01
  • 2019-08-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-01
  • 2018-06-30
相关资源
最近更新 更多