1、经典抽象工厂模式
下面程序,用以生成简单的“示意图”(diagram)。程序中用到两个“工厂”(factory):一个用来生成纯文本格式示意图,一个用来生成SVG格式示意图。
首先来看main()函数。
def main():
......//此处创建文件textFilename svgFilename
txtDiagram = create_diagram(DiagramFactory())
txtDiagram.save(textFilename)
svgDiagram = create_diagram(SvgDiagramFactory())
svgDiagram.save(svgFilename)
首先创建两个文件。接下来,用默认的出文本工厂创建示意图,并将其保存。然后,用SVG工厂来创建同样的示意图,也将其保存。
def create_diagram(factory):
diagram = factory.make_diagram(30,7)
rectangle = factory.make_rectangle(4,1,22,5,"yellow")
text = factory.make_text(7,3,"Abstract Factory")
diagram.add(rectangle)
diagram.add(text)
return diagram
create_diagram函数只有一个参数,即绘图所用的工厂,该函数用这个工厂创建出所需的示意图。此函数并不知道工厂的具体类型,也无需关心这一点,它只需要知道工厂对象具备创建示意图所需的接口即可(以make开头的那些方法)。
下面的工厂类用来绘制纯文本示意图:
class DiagramFactory:
def make_diagram(self,width,height):
return Diagram(width,height)
def make_rectangle(self,x,y,width,height,fill="white",
stroke="black"):
return Rectangle(x,y,width,height,fill,stroke)
def make_text(self,x,y,text,fontsize=12):
return Text(x,y,text,fontsize)
DiagramFactory类既是定义抽象接口的积累,又是提供实现代码的具体类。
下面这个类创建SVG示意图:
class SvgDiagramFactory(DiagramFactory):
def make_diagram(self,width,height):
return SvgDiagram(width,height)
......//下面代码与DiagramFactory类似
两个make_diagram之间的唯一区别:DiagramFactory.make_diagram()方法返回Diagram对象,SvgDiagramFactory.make_diagram()方法返回SvgDiagram对象。SvgDiagramFactory的另两个make方法也是如此。
虽然对应类的接口都一样(如Diagram与SvgDiagram类的方法名都相同),但是绘制纯文本示意图所用的Diagram、Rectangle、Text等类的实现方式与SVG示意图所用的SvgDiagram、SvgRectangle、SvgText等类截然不同。故不同系列的类之间不可混搭,工厂类会自行保证这一点。
纯文本Text对象:
class Text:
def __init__(self,x,y,text,fontsize):
self.x = x
self.y = y
self.rows = [list(text)]
纯文本Diagram对象:
class Diagram:
...//此处为__init__函数定义
def add(self,component):
for y,row in enumerate(component.rows):
for x,char in enumerate(row):
self.diagram[y + component.y][x + component.x]
= char
上面的Diagram.add()方法,component参数可能会是Rectangle或Text对象,该方法会遍历component里的二维列表(也就是component.rows),用其中的数据来替换示意图相应位置上的字符。示意图本身的字符由Diagram._init_()方法绘制。
SVG_TEXT = """<text x="{x}" y="{y}" text-anchor="left" \
font-family="sans-serif" font-size="{fountsize}">{text}</text>"""
SVG_SCALE = 20
class SvgText:
def __init__(self,x,y,text,fontsize):
x *=SVG_SCALE
y *=SVG_SCALE
fontsize *=SVG_SCALE
self.svg = SVG_TEXT.format(**locals())
//SVG_TEXT.format(x=x,y=y,text=text,fontsize=fontsize)
//SVG_TEXT.format_map(locals())
SvgDiagram类:
class SvgDiagram:
...//此处为__init__函数
def add(self,component):
self.diagram.append(component.svg)
SvgDiagram类的每个实例都有一份字符串列表,名叫self.diagram,列表中的每个字符串都表示一行SVG文本。
2、Python风格的抽象工厂模式
第一节中DiagramFactory和其子类SvgDiagramFactory的实现有几个缺点:
1、建立了两个工厂实例,但不需要。
2、两个工厂类的代码基本一样,代码重复了。
3、DiagramFactory、Diagram、Rectangle、Text类以及SVG系列中的与其对应的那些类都放在“顶级命名空间”里,没有必要。
故,修正上述缺陷,改动以下几点:
1、把Diagram、Rectangle、Text等类都嵌套到DiagramFactory类里面,SvgDiagram、SvgRectangle、SvgText等类嵌套到SvgDiagramFactory里面,解决顶级命名空间问题。
2、通过将make系列方法变为类方法,通过工厂类而不是工厂实例访问,解决代码重复和创建多余实例问题。
class DiagramFactory:
......
@classmethod
def make_diagram(Class,width,height):
return Class.Diagram(width,height)
@classmethod
def make_rectangle(Class,x,y,width,height,fill="white",
stroke="black"):
return Class.Rectangle(x,y,width,height,fill,stroke)
@classmethod
def make_text(Class,x,y,text,fontsize=12):
return Class.Text(x,y,text,fontsize)
class Text:
def __init__(self,x,y,text,fontsize):
self.x = x
self.y = y
self.rows = [list(text)]
......
SvgDiagramFactory不需实现make系列函数:
class SvgDiagramFactory(DiagramFactory):
......
SVG_TEXT = """<text x="{x}" y="{y}" text-anchor="left" \
font-family="sans-serif" font-size="{fountsize}">
{text}</text>"""
SVG_SCALE = 20
class Text:
def __init__(self,x,y,text,fontsize):
x *=SvgDiagramFactory.SVG_SCALE
y *=SvgDiagramFactory.SVG_SCALE
fontsize *=SvgDiagramFactory.SVG_SCALE
self.svg = SvgDiagramFactory.SVG_TEXT.format(**locals())
如此Main()中的调用将不会再使用工厂实例:
def main():
......//此处创建文件textFilename svgFilename
txtDiagram = create_diagram(DiagramFactory)
txtDiagram.save(textFilename)
svgDiagram = create_diagram(SvgDiagramFactory)
svgDiagram.save(svgFilename)