[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()函数也可以简化,因为现在不需要再创建工厂类的实例了。

附录:

 #!/usr/bin/env python3
# Copyright 漏 2012-13 Qtrac Ltd. All rights reserved.
# This program or module is free software: you can redistribute it
# and/or modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version. It is provided for
# educational purposes and is distributed in the hope that it will be
# useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details. 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) 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 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) 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) 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)] 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_RECTANGLE = """<rect x="{x}" y="{y}" width="{width}" \
height="{height}" fill="{fill}" stroke="{stroke}"/>""" SVG_TEXT = """<text x="{x}" y="{y}" text-anchor="left" \
font-family="sans-serif" font-size="{fontsize}">{text}</text>""" 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() 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()) 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()

diagram1

 #!/usr/bin/env python3
# Copyright 漏 2012-13 Qtrac Ltd. All rights reserved.
# This program or module is free software: you can redistribute it
# and/or modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version. It is provided for
# educational purposes and is distributed in the hope that it will be
# useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details. 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) 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 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) BLANK = " "
CORNER = "+"
HORIZONTAL = "-"
VERTICAL = "|" class Diagram: def __init__(self, width, height):
self.width = width
self.height = height
self.diagram = DiagramFactory._create_rectangle(self.width,
self.height, DiagramFactory.BLANK) 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() class Rectangle: def __init__(self, x, y, width, height, fill, stroke):
self.x = x
self.y = y
self.rows = DiagramFactory._create_rectangle(width, height,
DiagramFactory.BLANK if fill == "white" else "%") class Text: def __init__(self, x, y, text, fontsize):
self.x = x
self.y = y
self.rows = [list(text)] 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] = DiagramFactory.HORIZONTAL
rows[height - 1][x] = DiagramFactory.HORIZONTAL
for y in range(1, height - 1):
rows[y][0] = DiagramFactory.VERTICAL
rows[y][width - 1] = DiagramFactory.VERTICAL
for y, x in ((0, 0), (0, width - 1), (height - 1, 0),
(height - 1, width -1)):
rows[y][x] = DiagramFactory.CORNER
return rows class SvgDiagramFactory(DiagramFactory): # The make_* class methods are inherited 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_RECTANGLE = """<rect x="{x}" y="{y}" width="{width}" \
height="{height}" fill="{fill}" stroke="{stroke}"/>""" SVG_TEXT = """<text x="{x}" y="{y}" text-anchor="left" \
font-family="sans-serif" font-size="{fontsize}">{text}</text>""" SVG_SCALE = 20 class Diagram: def __init__(self, width, height):
pxwidth = width * SvgDiagramFactory.SVG_SCALE
pxheight = height * SvgDiagramFactory.SVG_SCALE
self.diagram = [SvgDiagramFactory.SVG_START.format(**locals())]
outline = SvgDiagramFactory.Rectangle(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" + SvgDiagramFactory.SVG_END)
finally:
if isinstance(filenameOrFile, str) and file is not None:
file.close() class Rectangle: def __init__(self, x, y, width, height, fill, stroke):
x *= SvgDiagramFactory.SVG_SCALE
y *= SvgDiagramFactory.SVG_SCALE
width *= SvgDiagramFactory.SVG_SCALE
height *= SvgDiagramFactory.SVG_SCALE
self.svg = SvgDiagramFactory.SVG_RECTANGLE.format(**locals()) class Text: def __init__(self, x, y, text, fontsize):
x *= SvgDiagramFactory.SVG_SCALE
y *= SvgDiagramFactory.SVG_SCALE
fontsize *= SvgDiagramFactory.SVG_SCALE // 10
self.svg = SvgDiagramFactory.SVG_TEXT.format(**locals()) if __name__ == "__main__":
main()

diagram2

上一篇:Jade之Interpolation


下一篇:C#简单的四位纯数字验证码