(本文为学习Python编程实战所作的笔记)
抽象工厂模式,用来创建复杂的对象,这种对象由许多小对象组成,而这些小对象都属于某个特定的系列。 比如说,在GUI系统中可以设计 “抽象控件工厂”,并设计具体子类工厂,它们提供创建同一对象的方法,而具体创建出来的对象风格与操作系统相符。
1.1.1 UML 1.1.2 Python实现 为了演示抽象工厂模式,用以生成简单是示意图,这段程序会用到"两个工厂": 一个生成纯文本格式示意图; 另一个用来生成SVG格式的示意图; (1)定义纯生成SVG格式的工厂:
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)从其实现的对象所定义类中,可以看出,该"工厂",所生产的产品包括“make_diagram”,“make_rectangle”,“make_text”。 因此还需要定义相关对象的实现方法; (2)定义"产品"实现方法:
Diagram:
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()SvgRectangle和SvgText:
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())(2)定义生成纯文本格式示意图的工厂:
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)Rectangle:
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 "%")Text:
class Text: def __init__(self, x, y, text, fontsize): self.x = x self.y = y self.rows = [list(text)]编写主函数,对调用"工厂"生成相应的产品:
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)1.2.1 UML类图 同上 1.2.2 Python实现 较之于1.2.1所描述的代码加入了一些Python的特性,主要区别是将产品的实现方法,封装在各自工厂类中,同时利用 @classmethod方法来实现,调用各自类中的参数: (1)定义TXT格式工厂类:
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(2)定义SVG格式工厂类:
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())