首页 > 代码库 > [Python编程实战] 第一章 python的创建型设计模式1.1抽象工厂模式

[Python编程实战] 第一章 python的创建型设计模式1.1抽象工厂模式

注:关乎对象的创建方式的设计模式就是“创建型设计模式”(creational design pattern)

 

1.1 抽象工厂模式

   “抽象工厂模式”(Abstract Factory Pattern)用来创建复杂的对象,这种对象由许多小对象组成,而这些小对象都属于某个特定的“系列”(family)。

    比如说,在GUI 系统里可以设计“抽象控件工厂”(abstract widget factory),并设计三个“具体子类工厂”(concrete subclass factory):MacWidgetFactory、XfceWidgetFactory、WindowsWidgetFactory,它们都创建同一种对象的方法(例如都提供创建按钮的make_button()方法,都提供创建数值调整框的make_spinbox()方法),而具体创建出来的风格则和操作系统平台相符。我们可以编写create_dialog()函数,令其以“工厂实例”(factory instance)为参数来创建OS X、Xfce及Windows风格的对话框,对话框的具体风格取决于传进来的工厂参数。

      1.1.1经典的工厂模式

       self.svg = SVG_TEXT.format(**locals())

       其中,使用**locals()的好处是比较省事,这样就不用再写成SVG_TEXT.format(x=x, y=y, text=text, fontsize=fontsize)了。且从Python3.2开始,还可以把SVG_TEXT.format(**locals())写成SVG_TEXT.format_map(locals()),因为str.format_map()方法会自动执行“映射解包”(mapping unpacking)操作。

      1.1.2 Python风格的工厂模式

       之前的写法有几个缺点:

       a.两个工厂都没有各自的状态,所以根本不需要创建实例。

       b.SvgDiagramFactory与DiagramFactory的代码基本上一模一样,只不过前者的make_diagram方法返回SvgText实例,而后者返回Text实例,其他的方法也如此,这会产生许多无谓的重复的代码。最后,DiagramFactory、Diagram、Rectangle、Text类以及SVG系列中与其对应的那些类都放在了“顶级命名空间”(top-level namespace)里

       c.给SVG的组建类起名时,需加前缀,使得代码显得不够整洁。

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)

        以make 开头的方法现在都变成了“类方法”(class method)。也就是说,调用这些方法时,其首个参数是类,而不像普通的方法那样,首个参数是self。例如,当调用DiagramFactory.make_text()方法时,Class参数就是DiagramFactory,此方法会创建DiagramFactory.Text对象并将其返回。

        这种方法使得SvgDiagramFactory子类只需继承DiagramFactory,而不用再去实现那几个make方法了。

                

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)

经过上述改变,main()函数也可以简化,因为现在不需要再创建工厂类的实例了。

附录:

技术分享
  1 #!/usr/bin/env python3
  2 # Copyright 漏 2012-13 Qtrac Ltd. All rights reserved.
  3 # This program or module is free software: you can redistribute it
  4 # and/or modify it under the terms of the GNU General Public License as
  5 # published by the Free Software Foundation, either version 3 of the
  6 # License, or (at your option) any later version. It is provided for
  7 # educational purposes and is distributed in the hope that it will be
  8 # useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
  9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 10 # General Public License for more details.
 11 
 12 import os
 13 import sys
 14 import tempfile
 15 
 16 
 17 def main():
 18     if len(sys.argv) > 1 and sys.argv[1] == "-P": # For regression testing
 19         create_diagram(DiagramFactory()).save(sys.stdout)
 20         create_diagram(SvgDiagramFactory()).save(sys.stdout)
 21         return
 22     textFilename = os.path.join(tempfile.gettempdir(), "diagram.txt")
 23     svgFilename = os.path.join(tempfile.gettempdir(), "diagram.svg")
 24 
 25     txtDiagram = create_diagram(DiagramFactory())
 26     txtDiagram.save(textFilename)
 27     print("wrote", textFilename)
 28 
 29     svgDiagram = create_diagram(SvgDiagramFactory())
 30     svgDiagram.save(svgFilename)
 31     print("wrote", svgFilename)
 32 
 33 
 34 def create_diagram(factory):
 35     diagram = factory.make_diagram(30, 7)
 36     rectangle = factory.make_rectangle(4, 1, 22, 5, "yellow")
 37     text = factory.make_text(7, 3, "Abstract Factory")
 38     diagram.add(rectangle)
 39     diagram.add(text)
 40     return diagram
 41 
 42 
 43 class DiagramFactory:
 44 
 45     def make_diagram(self, width, height):
 46         return Diagram(width, height)
 47 
 48 
 49     def make_rectangle(self, x, y, width, height, fill="white",
 50             stroke="black"):
 51         return Rectangle(x, y, width, height, fill, stroke)
 52 
 53 
 54     def make_text(self, x, y, text, fontsize=12):
 55         return Text(x, y, text, fontsize)
 56 
 57 
 58 
 59 class SvgDiagramFactory(DiagramFactory):
 60 
 61     def make_diagram(self, width, height):
 62         return SvgDiagram(width, height)
 63 
 64 
 65     def make_rectangle(self, x, y, width, height, fill="white",
 66             stroke="black"):
 67         return SvgRectangle(x, y, width, height, fill, stroke)
 68 
 69 
 70     def make_text(self, x, y, text, fontsize=12):
 71         return SvgText(x, y, text, fontsize)
 72 
 73 
 74 BLANK = " "
 75 CORNER = "+"
 76 HORIZONTAL = "-"
 77 VERTICAL = "|"
 78 
 79 
 80 class Diagram:
 81 
 82     def __init__(self, width, height):
 83         self.width = width
 84         self.height = height
 85         self.diagram = _create_rectangle(self.width, self.height, BLANK)
 86 
 87 
 88     def add(self, component):
 89         for y, row in enumerate(component.rows):
 90             for x, char in enumerate(row):
 91                 self.diagram[y + component.y][x + component.x] = char
 92 
 93 
 94     def save(self, filenameOrFile):
 95         file = None if isinstance(filenameOrFile, str) else filenameOrFile
 96         try:
 97             if file is None:
 98                 file = open(filenameOrFile, "w", encoding="utf-8")
 99             for row in self.diagram:
100                 print("".join(row), file=file)
101         finally:
102             if isinstance(filenameOrFile, str) and file is not None:
103                 file.close()
104 
105 
106 def _create_rectangle(width, height, fill):
107     rows = [[fill for _ in range(width)] for _ in range(height)]
108     for x in range(1, width - 1):
109         rows[0][x] = HORIZONTAL
110         rows[height - 1][x] = HORIZONTAL
111     for y in range(1, height - 1):
112         rows[y][0] = VERTICAL
113         rows[y][width - 1] = VERTICAL
114     for y, x in ((0, 0), (0, width - 1), (height - 1, 0),
115             (height - 1, width -1)):
116         rows[y][x] = CORNER
117     return rows
118 
119 
120 class Rectangle:
121 
122     def __init__(self, x, y, width, height, fill, stroke):
123         self.x = x
124         self.y = y
125         self.rows = _create_rectangle(width, height,
126                 BLANK if fill == "white" else "%")
127 
128 
129 class Text:
130 
131     def __init__(self, x, y, text, fontsize):
132         self.x = x
133         self.y = y
134         self.rows = [list(text)]
135 
136 
137 SVG_START = """<?xml version="1.0" encoding="UTF-8" standalone="no"?>
138 <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
139     "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
140 <svg xmlns="http://www.w3.org/2000/svg"
141     xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve"
142     width="{pxwidth}px" height="{pxheight}px">"""
143 
144 SVG_END = "</svg>\n"
145 
146 SVG_RECTANGLE = """<rect x="{x}" y="{y}" width="{width}" 147 height="{height}" fill="{fill}" stroke="{stroke}"/>"""
148 
149 SVG_TEXT = """<text x="{x}" y="{y}" text-anchor="left" 150 font-family="sans-serif" font-size="{fontsize}">{text}</text>"""
151 
152 SVG_SCALE = 20
153 
154 
155 class SvgDiagram:
156 
157 
158     def __init__(self, width, height):
159         pxwidth = width * SVG_SCALE
160         pxheight = height * SVG_SCALE
161         self.diagram = [SVG_START.format(**locals())]
162         outline = SvgRectangle(0, 0, width, height, "lightgreen", "black")
163         self.diagram.append(outline.svg)
164 
165 
166     def add(self, component):
167         self.diagram.append(component.svg)
168 
169 
170     def save(self, filenameOrFile):
171         file = None if isinstance(filenameOrFile, str) else filenameOrFile
172         try:
173             if file is None:
174                 file = open(filenameOrFile, "w", encoding="utf-8")
175             file.write("\n".join(self.diagram))
176             file.write("\n" + SVG_END)
177         finally:
178             if isinstance(filenameOrFile, str) and file is not None:
179                 file.close()
180 
181 
182 class SvgRectangle:
183 
184     def __init__(self, x, y, width, height, fill, stroke):
185         x *= SVG_SCALE
186         y *= SVG_SCALE
187         width *= SVG_SCALE
188         height *= SVG_SCALE
189         self.svg = SVG_RECTANGLE.format(**locals())
190 
191 
192 class SvgText:
193 
194     def __init__(self, x, y, text, fontsize):
195         x *= SVG_SCALE
196         y *= SVG_SCALE
197         fontsize *= SVG_SCALE // 10
198         self.svg = SVG_TEXT.format(**locals())
199 
200 
201 if __name__ == "__main__":
202     main()
diagram1
技术分享
  1 #!/usr/bin/env python3
  2 # Copyright 漏 2012-13 Qtrac Ltd. All rights reserved.
  3 # This program or module is free software: you can redistribute it
  4 # and/or modify it under the terms of the GNU General Public License as
  5 # published by the Free Software Foundation, either version 3 of the
  6 # License, or (at your option) any later version. It is provided for
  7 # educational purposes and is distributed in the hope that it will be
  8 # useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
  9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 10 # General Public License for more details.
 11 
 12 import os
 13 import sys
 14 import tempfile
 15 
 16 
 17 def main():
 18     if len(sys.argv) > 1 and sys.argv[1] == "-P": # For regression testing
 19         create_diagram(DiagramFactory).save(sys.stdout)
 20         create_diagram(SvgDiagramFactory).save(sys.stdout)
 21         return
 22     textFilename = os.path.join(tempfile.gettempdir(), "diagram.txt")
 23     svgFilename = os.path.join(tempfile.gettempdir(), "diagram.svg")
 24 
 25     txtDiagram = create_diagram(DiagramFactory)
 26     txtDiagram.save(textFilename)
 27     print("wrote", textFilename)
 28 
 29     svgDiagram = create_diagram(SvgDiagramFactory)
 30     svgDiagram.save(svgFilename)
 31     print("wrote", svgFilename)
 32 
 33 
 34 def create_diagram(factory):
 35     diagram = factory.make_diagram(30, 7)
 36     rectangle = factory.make_rectangle(4, 1, 22, 5, "yellow")
 37     text = factory.make_text(7, 3, "Abstract Factory")
 38     diagram.add(rectangle)
 39     diagram.add(text)
 40     return diagram
 41 
 42 
 43 class DiagramFactory:
 44 
 45     @classmethod
 46     def make_diagram(Class, width, height):
 47         return Class.Diagram(width, height)
 48 
 49 
 50     @classmethod
 51     def make_rectangle(Class, x, y, width, height, fill="white",
 52             stroke="black"):
 53         return Class.Rectangle(x, y, width, height, fill, stroke)
 54 
 55     @classmethod
 56     def make_text(Class, x, y, text, fontsize=12):
 57         return Class.Text(x, y, text, fontsize)
 58 
 59 
 60     BLANK = " "
 61     CORNER = "+"
 62     HORIZONTAL = "-"
 63     VERTICAL = "|"
 64 
 65 
 66     class Diagram:
 67 
 68         def __init__(self, width, height):
 69             self.width = width
 70             self.height = height
 71             self.diagram = DiagramFactory._create_rectangle(self.width,
 72                     self.height, DiagramFactory.BLANK)
 73 
 74 
 75         def add(self, component):
 76             for y, row in enumerate(component.rows):
 77                 for x, char in enumerate(row):
 78                     self.diagram[y + component.y][x + component.x] = char
 79 
 80 
 81         def save(self, filenameOrFile):
 82             file = (None if isinstance(filenameOrFile, str) else
 83                     filenameOrFile)
 84             try:
 85                 if file is None:
 86                     file = open(filenameOrFile, "w", encoding="utf-8")
 87                 for row in self.diagram:
 88                     print("".join(row), file=file)
 89             finally:
 90                 if isinstance(filenameOrFile, str) and file is not None:
 91                     file.close()
 92 
 93 
 94     class Rectangle:
 95 
 96         def __init__(self, x, y, width, height, fill, stroke):
 97             self.x = x
 98             self.y = y
 99             self.rows = DiagramFactory._create_rectangle(width, height,
100                     DiagramFactory.BLANK if fill == "white" else "%")
101 
102 
103     class Text:
104 
105         def __init__(self, x, y, text, fontsize):
106             self.x = x
107             self.y = y
108             self.rows = [list(text)]
109 
110 
111     def _create_rectangle(width, height, fill):
112         rows = [[fill for _ in range(width)] for _ in range(height)]
113         for x in range(1, width - 1):
114             rows[0][x] = DiagramFactory.HORIZONTAL
115             rows[height - 1][x] = DiagramFactory.HORIZONTAL
116         for y in range(1, height - 1):
117             rows[y][0] = DiagramFactory.VERTICAL
118             rows[y][width - 1] = DiagramFactory.VERTICAL
119         for y, x in ((0, 0), (0, width - 1), (height - 1, 0),
120                 (height - 1, width -1)):
121             rows[y][x] = DiagramFactory.CORNER
122         return rows
123 
124 
125 class SvgDiagramFactory(DiagramFactory):
126 
127     # The make_* class methods are inherited
128 
129     SVG_START = """<?xml version="1.0" encoding="UTF-8" standalone="no"?>
130 <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
131     "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
132 <svg xmlns="http://www.w3.org/2000/svg"
133     xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve"
134     width="{pxwidth}px" height="{pxheight}px">"""
135 
136     SVG_END = "</svg>\n"
137 
138     SVG_RECTANGLE = """<rect x="{x}" y="{y}" width="{width}" 139 height="{height}" fill="{fill}" stroke="{stroke}"/>"""
140 
141     SVG_TEXT = """<text x="{x}" y="{y}" text-anchor="left" 142 font-family="sans-serif" font-size="{fontsize}">{text}</text>"""
143 
144     SVG_SCALE = 20
145 
146 
147     class Diagram:
148 
149         def __init__(self, width, height):
150             pxwidth = width * SvgDiagramFactory.SVG_SCALE
151             pxheight = height * SvgDiagramFactory.SVG_SCALE
152             self.diagram = [SvgDiagramFactory.SVG_START.format(**locals())]
153             outline = SvgDiagramFactory.Rectangle(0, 0, width, height,
154                     "lightgreen", "black")
155             self.diagram.append(outline.svg)
156 
157 
158         def add(self, component):
159             self.diagram.append(component.svg)
160 
161 
162         def save(self, filenameOrFile):
163             file = (None if isinstance(filenameOrFile, str) else
164                     filenameOrFile)
165             try:
166                 if file is None:
167                     file = open(filenameOrFile, "w", encoding="utf-8")
168                 file.write("\n".join(self.diagram))
169                 file.write("\n" + SvgDiagramFactory.SVG_END)
170             finally:
171                 if isinstance(filenameOrFile, str) and file is not None:
172                     file.close()
173 
174 
175     class Rectangle:
176 
177         def __init__(self, x, y, width, height, fill, stroke):
178             x *= SvgDiagramFactory.SVG_SCALE
179             y *= SvgDiagramFactory.SVG_SCALE
180             width *= SvgDiagramFactory.SVG_SCALE
181             height *= SvgDiagramFactory.SVG_SCALE
182             self.svg = SvgDiagramFactory.SVG_RECTANGLE.format(**locals())
183 
184 
185     class Text:
186 
187         def __init__(self, x, y, text, fontsize):
188             x *= SvgDiagramFactory.SVG_SCALE
189             y *= SvgDiagramFactory.SVG_SCALE
190             fontsize *= SvgDiagramFactory.SVG_SCALE // 10
191             self.svg = SvgDiagramFactory.SVG_TEXT.format(**locals())
192 
193 
194 if __name__ == "__main__":
195     main()
diagram2

 

                                        

[Python编程实战] 第一章 python的创建型设计模式1.1抽象工厂模式