工厂设计模式

ajk*_*hol 13 python design-patterns

我正在尝试实施工厂设计模式,并且到目前为止已经做到了这一点.

import abc

class Button(object):
    __metaclass__ = abc.ABCMeta

    html = ""
    def get_html(self, html):
        return self.html

class ButtonFactory():
    def create_button(self, type):
        baseclass = Button()
        targetclass = type.baseclass.capitalize()
        return targetclass

button_obj = ButtonFactory()
button = ['image', 'input', 'flash']
for b in button:
    print button_obj.create_button(b).get_html()
Run Code Online (Sandbox Code Playgroud)

输出应该是所有按钮类型的HTML.

我得到这样的错误

AttributeError: 'str' object has no attribute 'baseclass'
Run Code Online (Sandbox Code Playgroud)

我正在尝试实现一个具有不同变体的类,例如ImageButton,InputButton和FlashButton.根据地点的不同,可能需要为按钮创建不同的html

Eli*_*sha 13

您试图调用不存在的baseclass属性str,因为b获取字符串值(其中之一['image', 'input', 'flash']).如果要根据表示其名称的字符串创建对象,可以使用globals()字典,该字典包含变量名称及其值之间的映射.

class Button(object):
    html = ""
    def get_html(self):
        return self.html

class Image(Button):
    html = "<img></img>"

class Input(Button):
    html = "<input></input>"

class Flash(Button):
    html = "<obj></obj>"

class ButtonFactory():
    def create_button(self, typ):
        targetclass = typ.capitalize()
        return globals()[targetclass]()

button_obj = ButtonFactory()
button = ['image', 'input', 'flash']
for b in button:
    print button_obj.create_button(b).get_html()
Run Code Online (Sandbox Code Playgroud)

编辑:使用globals()locals()不是一个好的做法所以,如果可以的话,最好在相关对象和它们的名称之间创建一个映射,如下所示:

button_objects = {'image':Image,'flash':Flash,'input':Input}
Run Code Online (Sandbox Code Playgroud)

并替换create_button为:

def create_button(self, typ):        
    return button_objects[typ]()
Run Code Online (Sandbox Code Playgroud)

  • 我强烈建议创建一个明确映射的字典,例如``image'`到`Image`,而不是用`eval'动态地映射.或者更好的是,只需使用`button = [Image,Input,Flash]`:类是可以存储在列表中的第一类对象. (5认同)

pck*_*ko1 5

截至 2021 年,由于 Python 已经发展,已接受的答案可以通过多种方式进行改进 ( 3.9.x)。这同样适用于其未注明来源的原始来源

首先,对象工厂的实现与接口的概念紧密结合。根据工厂模式的四人帮定义,我们读到它应该:

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

话虽如此,对已接受答案的一些重要改进可能如下:

  1. 基类Button应该是一个抽象类来定义接口。换句话说,它应该继承自abc.ABC.

  2. 我们看到,子项 ( FlashImageInput) 之间的唯一区别是每种情况下属性的值self.html,而不是方法的实际实现get_html。这意味着self.html属性应该是抽象的,即接口的一部分。定义属性的一种非常巧妙的方法是通过内置@property装饰器。因此,抽象属性将使用@abc.abstractmethod@property其“getter”方法进行修饰。这很重要,因为如果“getter”方法没有在子类的主体内部实现,就会引发异常,从而有效地遵守接口规范。

  3. ButtonFactory.create_button方法是不必要的。事实上,也没有必要实例化 ButtonFactory,其唯一目的是生成子对象。ButtonFactory相反,您可以在 a 的构造函数内(即方法内部)迁移子对象的创建__new__。这将在工厂实例化时直接生成一个子对象。

  4. 最后,我们可以将所有按钮放在同一个命名空间(Buttons类)下,以避免globals在查找任何子按钮时使用。

将所有内容放在一个buttons.py文件中:

from abc import ABC, abstractmethod


class Buttons:
    class Button(ABC):
        @property
        @abstractmethod
        def html(self) -> str:
            raise NotImplementedError

    class Flash(Button):
        html = "<obj></obj>"

    class Image(Button):
        html = "<img></img>"

    class Input(Button):
        html = "<input></input>"


class ButtonFactory:
    def __new__(cls, button_type: str) -> Buttons.Button:
        return getattr(Buttons, button_type)()

Run Code Online (Sandbox Code Playgroud)

通过这种方式,我们可以将任何类似按钮的对象实例化为:

from buttons import ButtonFactory

flash_button = ButtonFactory("Flash")
image_button = ButtonFactory("Image")
input_button = ButtonFactory("Input")

print(flash_button.html, image_button.html, input_button.html)
Run Code Online (Sandbox Code Playgroud)

输出将是:

<obj></obj> <img></img> <input></input>
Run Code Online (Sandbox Code Playgroud)