Python抽象工厂模式

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)
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值