這又是一篇源自於學長在其『搞笑談軟工』部落格上的關於Design for testability問題,同樣這也引起網友的討論,但總覺得偏向於『對測試程式的修改』或是『使用第三方套件存取私有變數』,而不是修改程式讓程式變好測(Design for testability),所以就野人獻曝,提供一個我覺得比較合適的解法。問題中的WidgetFactoryV2類別同時肩負了Simple FactorySingleton的責任,前者負責針對Windows平台和Motif平台提供不同的factory instance;後者則讓factory在一個JVM只有一個instance。這樣的設計會讓測試不好測試,就如同問題中提到的,但事實上,這問題和Singleton是無關的,更貼切地說應該是『如何測試由平台引起的Simple Factory』,所以我將WidgetFactoryV2改成如圖1的SimpleWidgetFactory,只負責平台相依的問題,Singleton不再是它的責任,此外,我也將『平台』的資訊從程式內部直接呼叫System.getProperty()的方式,換成從參數傳入,如此一來,針對SimpleWidgetFactory就變得很容易測,如圖2所示。

SimpleWidgetFactory.png  
圖1 Simple Factory只負責平台相依問題

SimpleWidgetFactoryTests.png
圖2 SimpleWidgetFactory的測試案例 

  那Singleton變成誰的責任了?答案是由concrete factory自己負責,如圖3和圖4所示,雖然這會導致有些程式碼重複了(duplicated code壞味道),但這還在可以接受的範圍內,以物件生命週期的角度來看,自己管理自己的生命週期還是比較妥當一點。如此一來,Singleton的測試就簡單了:對同一類別呼叫二次getInstance(),檢查回傳的物件是不是同一個物件就結束了(圖5)。

Win32WidgetFactory.png
圖3 Win32WidgetFactory自己負責維護單一instance的責任

MotifWidgetFactory.png
圖4 MotifWidgetFactory自己負責維護單一instance的責任

FactorySingletonTests.png
圖5 針對Singleton的測試案例 

  好啦!對原先的設計做了些許的修改,提高了程式的可測性(Testability)大概就是這樣,可能有人會懷疑:『如果SimpleWidgetFactory不是呼叫getInstance(),而是直接new一個instance,這樣就能確保呼叫SimpleWidgetFactory.getFactory()就一定拿到同一個instance嗎?』針對這個問題...ㄜ...來人啊!把問這問題的人抓去讀Singleton 50遍,如果concrete factory真的實作Singleton,而且實作是正確的,那這個情形不可能發生,因為constructor早已被宣告成無法存取的層級(protected或private)。

  附錄:關於Simple FactoryFactory Method的差別,簡單地說,就是有沒有出現繼承,Simple Factory只是將四散在各地或是因平台相異的creation method集中在一個類別中,例如圖1,而Factory Method會有一個繼承架構,由一個介面(本例中的WidgetFactory)或抽象類別定義該有哪些creation methods,然後由繼承的類別(本例中的Win32WidgetFactoryMotifWidgetFactory)提供實作,至於creation methods是不是Template Method中的primitive operations,我個人沒有意見,可以是也可以不是,端看定義creation methods的抽象類別(Java中interface無法有實作)有沒有提供預設的skeleton實作和如何使用這些creation methods (變成Builder?),但個人傾向不使用這方法,原因一樣跟可測性有關,當一個類別承擔越多責任,除了內聚力下降外,也難以測試。

  題外話,如果真要說,WidgetFactoryWin32WidgetFactoryMotifWidgetFactory已經把Abstract Factory的『形』給建構出來了。

dbi1463 發表在 痞客邦 PIXNET 留言(0) 人氣()