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)
截至 2021 年,由于 Python 已经发展,已接受的答案可以通过多种方式进行改进 ( 3.9.x)。这同样适用于其未注明来源的原始来源。
首先,对象工厂的实现与接口的概念紧密结合。根据工厂模式的四人帮定义,我们读到它应该:
定义一个用于创建对象的接口,但让子类决定实例化哪个类。工厂方法让类将实例化推迟到子类。
话虽如此,对已接受答案的一些重要改进可能如下:
基类Button应该是一个抽象类来定义接口。换句话说,它应该继承自abc.ABC.
我们看到,子项 ( Flash、Image、Input) 之间的唯一区别是每种情况下属性的值self.html,而不是方法的实际实现get_html。这意味着self.html属性应该是抽象的,即接口的一部分。定义属性的一种非常巧妙的方法是通过内置@property装饰器。因此,抽象属性将使用@abc.abstractmethod和@property其“getter”方法进行修饰。这很重要,因为如果“getter”方法没有在子类的主体内部实现,就会引发异常,从而有效地遵守接口规范。
该ButtonFactory.create_button方法是不必要的。事实上,也没有必要实例化 ButtonFactory,其唯一目的是生成子对象。ButtonFactory相反,您可以在 a 的构造函数内(即方法内部)迁移子对象的创建__new__。这将在工厂实例化时直接生成一个子对象。
最后,我们可以将所有按钮放在同一个命名空间(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)