Python抽象工厂模式

来源:互联网 发布:php 扩展reflection 编辑:程序博客网 时间:2024/05/01 22:54

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 = 20class 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)
0 0