美文网首页
第一章 Python的创建型设计模式(1)

第一章 Python的创建型设计模式(1)

作者: Afen0 | 来源:发表于2019-01-17 21:21 被阅读0次

    1.1 抽象工厂模式 (Abstract Factory Pattern)

    在《Python in pratice》这本书中,“抽象工厂模式” 的解释是用来创建复杂的对象,而这种对象由许多的小对象组成,同时这些小对象都属于特定的系列。

    在主函数中,首先创建两个文件,接下来用默认的纯文本工厂和SVG工厂创建两个示意图,并将其保存。

    import os
    import sys
    import tempfile
    
    def main():
        if len(sys.argv) > 1 and sys.argv[1] == "-P": # For regression testing
            create_diagram(DiagramFactory()).save(sys.stdout)
            create_diagram(SvgDiagramFactory()).save(sys.stdout)
            return
        
        textFilename = os.path.join(tempfile.gettempdir(), "diagram.txt")
        svgFilename = os.path.join(tempfile.gettempdir(), "diagram.svg")
    
        txtDiagram = create_diagram(DiagramFactory())
        txtDiagram.save(textFilename)
        print("wrote", textFilename)
    
        svgDiagram = create_diagram(SvgDiagramFactory())
        svgDiagram.save(svgFilename)
        print("wrote", svgFilename)
    

    tempfile的作用是操作临时文件夹。应用程序经常要保存一些临时的信息,这些信息不是特别重要,没有必要写在配置文件里,但又不能没有,在windows操作系统中,临时文件一般被保存在:C:/Documents and Settings/User/Local Settings/Temp。

    create_diagram(factory)函数只有一个参数 factory,这个函数不关心工厂的具体类型,只需要知道工厂对象具备创建示意图所需要的接口就可以了。

    • 如果 factory = DiagramFactory, 就调用 DiagramFactory下的 make_digram, make_rectange 以及 make_text 三个方法;
    • 如果参数 factory = SvgDiagramFactory, 则调用 SvgDiagramFactory下的三个方法。
    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
    

    接下来就是实现两个工厂类了,这两个类十分相似,唯一的区别就是SvgDiagramFactory下的方法都有一个前缀Svg表示区分,这里主要是为了避免名称冲突(下一篇会讲优化方法);

    • make_digram 两个参数分别定义了外框的长度以及宽度;
    • make_rectange 前四个参数分别定义了内框跟外框的 x方向距离、y方向上的距
      离,长度以及宽度;
    • make_text 两个参数表示内框跟文字 x方向距离、y方向上的距离;
    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)
    
    
    class SvgDiagramFactory(DiagramFactory):
    
        def make_diagram(self, width, height):
            return SvgDiagram(width, height)
    
        def make_rectangle(self, x, y, width, height, fill="white", stroke="black"):
            return SvgRectangle(x, y, width, height, fill, stroke)
    
        def make_text(self, x, y, text, fontsize=12):
            return SvgText(x, y, text, fontsize)
    
    • 纯文本 Diagram 对象使用‘二维列表’对示意图中的数据进行保存,也就是下面四个符号:空格、+、-、| ;
    • 因为 Diagram 跟 Rectange 相似,所以只需要编写一个_create_rectangle(width, height, fill)函数供调用即可,其中采用列表推导式来生成二维列表,并根据开始定义的 +,-,| 把对应位置的空格替换;
    # 定义了空白处、转角、水平方向跟竖直方向的符号表示
    BLANK = " "
    CORNER = "+"
    HORIZONTAL = "-"
    VERTICAL = "|"
    
    class Diagram:
    
        def __init__(self, width, height):
            self.width = width
            self.height = height
            self.diagram = _create_rectangle(self.width, self.height, BLANK)
    
        # component的参数可以是Rectange或者Text对象
        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
    
        def save(self, filenameOrFile):
            file = None if isinstance(filenameOrFile, str) else filenameOrFile
            try:
                if file is None:
                    file = open(filenameOrFile, "w", encoding="utf-8")
                for row in self.diagram:
                    print("".join(row), file=file)
            finally:
                if isinstance(filenameOrFile, str) and file is not None:
                    file.close()
    
    def _create_rectangle(width, height, fill):
        # 共有的创建矩形的函数,采用列表推导式生成二维列表;
        rows = [[fill for _ in range(width)] for _ in range(height)]
        for x in range(1, width - 1):
            rows[0][x] = HORIZONTAL
            rows[height - 1][x] = HORIZONTAL
        for y in range(1, height - 1):
            rows[y][0] = VERTICAL
            rows[y][width - 1] = VERTICAL
        for y, x in ((0, 0), (0, width - 1), (height - 1, 0), (height - 1, width -1)):
            rows[y][x] = CORNER
        return rows
    
    class Rectangle:
    
        def __init__(self, x, y, width, height, fill, stroke):
            self.x = x
            self.y = y
            self.rows = _create_rectangle(width, height, BLANK if fill == "white" else "%")
    
    class Text:
    
        def __init__(self, x, y, text, fontsize):
            self.x = x
            self.y = y
            self.rows = [list(text)]
    
    • SvgDiagram类的每个实例都有一份字符串列表,名叫 self.diagram , 列表中每个字符串都表示一行SVG文本,这样一来就只需要append 新组件就可以了
    • 使用 --locals() 的好处是不必再将参数都写出来了,比较省事,在这里等价于SVG_START.format( width = pxwidth ,height = pxheight );
    SVG_START = """<?xml version="1.0" encoding="UTF-8" standalone="no"?>
    <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
        "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
    <svg xmlns="http://www.w3.org/2000/svg"
        xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve"
        width="{pxwidth}px" height="{pxheight}px">"""
    
    SVG_END = "</svg>\n"
    
    SVG_SCALE = 20
    
    class SvgDiagram:
        def __init__(self, width, height):
            pxwidth = width * SVG_SCALE
            pxheight = height * SVG_SCALE
            self.diagram = [SVG_START.format(**locals())]
            outline = SvgRectangle(0, 0, width, height, "lightgreen", "black")
            self.diagram.append(outline.svg)
    
        def add(self, component):
            self.diagram.append(component.svg)
    
        def save(self, filenameOrFile):
            file = None if isinstance(filenameOrFile, str) else filenameOrFile
            try:
                if file is None:
                    file = open(filenameOrFile, "w", encoding="utf-8")
                file.write("\n".join(self.diagram))
                file.write("\n" + SVG_END)
            finally:
                if isinstance(filenameOrFile, str) and file is not None:
                    file.close()
    

    然后是 SvgRectangle 类:

    SVG_RECTANGLE = """<rect x="{x}" y="{y}" width="{width}" \
    height="{height}" fill="{fill}" stroke="{stroke}"/>"""
    
    class SvgRectangle:
        def __init__(self, x, y, width, height, fill, stroke):
            x *= SVG_SCALE
            y *= SVG_SCALE
            width *= SVG_SCALE
            height *= SVG_SCALE
            self.svg = SVG_RECTANGLE.format(**locals())
    

    然后是 SvgText 类:

    SVG_TEXT = """<text x="{x}" y="{y}" text-anchor="left" \
    font-family="sans-serif" font-size="{fontsize}">{text}</text>"""
    
    class SvgText:
        def __init__(self, x, y, text, fontsize):
            x *= SVG_SCALE
            y *= SVG_SCALE
            fontsize *= SVG_SCALE // 10
            self.svg = SVG_TEXT.format(**locals())
    
    if __name__ == "__main__":
        main()
    

    相关文章

      网友评论

          本文标题:第一章 Python的创建型设计模式(1)

          本文链接:https://www.haomeiwen.com/subject/gmewdqtx.html