上一篇文章我们说到DiagramFactory和其SvgDiagramFactory子类以及它们使用到的类比如(Diagram,SvgDiagram等等),能够很好的实现预订的功能并且也符合抽象工厂的设计模式。
然而,我们的实现并非是非常完美的,至少还有以下几点不足:
1)我们并不需要保存每个工厂的状态,因此,在向create_diagram传递参数的时候,就没有必要传递工厂的实例。
2)SvgDiagramFactory的代码几乎和DiagramFactory完全一样,唯一的区别就在于其返回值在Text或Rectangle的前面加了Svg的前缀,即:SvgText SvgRectangle等等。
3)我们的顶层命名空间中保存了所有的类:DiagramFactory、Digram、Rectangle、TeXT以及所有的SVG相关的类。然而,实际上我们真正需要从外部调用的仅有两个工厂类而已。
4)为了避免类名称的冲突,我们不得不在子类的名称前面添加SVG前缀,实际而言,这样的代码看起来不是很整洁。(解决类名称冲突的另一个解决办法是:把每个类放到自己的模块内部,但是这样做却依然解决不了代码重复的问题)。
本文就着力解决上述四点问题。
我们所要做的第一个改变就是将Diagram,Rectangle及Text类封装进DiagramFactory类中。这就意味着只能用类似于DiagramFactory.Diagram的形式访问这些类。但是,现在我们就就可以为这些类起相同的名字了,因为类名冲突将不复存在,比如SvgDiagramFactory.Diagram。我们也把类所依赖的一些常量封装进了内部,所有,在本模块的内部顶级名称只有:main(), create_diagram(), DiagramFactory和SvgDiagramFactory。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)
...
这是我们的新的DiagramFactory类。其中的make_...()方法全部都是类方法。这也就意味着。当被调用时,类将作为第一个参数传递给该函数(就像self被传递给普通类方法一样)。所以,在这种情况下调用DiagramFactory.make_text()就意味着DiagramFactory作为class参数被传递进去,从而一个DiagramFactory.Text对象被创建并返回。
这个改变也意味着,作为派生自DiagramFactory的SvgDiagramFactory类,不再需要自己实现make_...()的函数。比如说,当我们调用SvgDiagramFactory.make_rectangle()这个方法,由于SvgDiagramFactory里并没有该方法,因此就会调用其基类的DiagramFactoy.make_rectangle()方法,但是传进去的class参数确是SvgDiagramFactory.因此,就导致SvgDiagramFactory.Rectangle对象创建并返回。def main(): ...
txtDiagram = create_diagram(DiagramFactory)
txtDiagram.save(textFilename)
svgDiagram = create_diagram(SvgDiagramFactory)
svgDiagram.save(svgFilename)
这些改变也意味着我们可以简化main()函数的设计,因为我们无需再创建factory工厂的实例了。
其余的代码基本和之前一样。比较明显的区别就在于因为我们将常量和非工厂类都封装进工厂的类代码里,我们现在必须使用工厂名称来访问它们。
class SvgDiagramFactory(DiagramFactory):
...
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())
这是封装进SvgDiagramFactory类的代码里的Text类,演示了封装的常量是如何被访问的。
好了,这就是抽象工厂代码的的进一步优化,后面我们还会涉足其他设计模式,敬请期待!