上一篇文章我們說到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類,演示了封裝的常量是如何被訪問的。
好了,這就是抽象工廠代碼的的進一步優化,後面我們還會涉足其他設計模式,敬請期待!