套用「Simple Factory Method」設計模式,可以降低用戶端程式碼與所欲使用的產品之間的關聯性。
用戶端只需明白各種實際產品所依循的共通規範(產品介面),並且加以運用即可。如此一來,當實際產品有所更動,或產生實際產品的邏輯有所變化時,用戶端程式碼也不致於被這更動影響,這正是「Simple Factory Method」的好處。
但是在系統中,仍舊有某個類別有可能和這兩樣邏輯存在相依性,它就是提供「Simple Factory Method」的這個類別。
在前一回中,我們設計了一個名為AuthenticatorFactory的類別,負責產生各式認證類別的實作。此AuthenticatorFactory介面如下:
createAuthenticator()會產生系統目前所設定相對應的認證類別,以下是一個可能實作的方式:
這個method會利用一連串的if-else if比對它所支援的產品──也就是認證類別,倘若authType字串與它所支援的類型相符,便產生相對應的Authenticator實作(例如LDAPAuthentictor)並且回傳。倘若找不到相符者,便回傳null。
我們時常可以在「Simple Factory Method」中看到不是一連串if-else if的判斷述句,就是switch-case的判斷,因為「Simple Factory Method」必須決定要產生那一種產品。
然而,這樣子的寫法尚有缺憾。當我們要新增一項可能的產品,或者判斷方式有所改變時,雖然用戶端程式碼可以逃過更動的命運,但是提供「Simple Factory Method」的類別,卻難以免除修改的宿命。
雖然用戶端程式碼毋需更動,但createAuthenticator()仍舊被改變所擊中,它得加上以下程式:
因為createAuthenticator()知道這些事情,所以當這些事情改變時,就會影響到它。
如果想避免提供「Simple Factory Method」的類別被改變,需要進一步的技巧。最常運用的是利用所謂「Reflection」機制。何謂「Reflection」?依照Wikipedia上的解釋,「Reflection」就是在執行時,依據程式碼的抽象特性及執行期行為,能夠隨之更改的程式。
以比喻的方式來說,就是一種能夠自我觀照並修改自身結構及行為的程式。「Reflection」自身便是一門博大精深的學問,我們試著利用最基礎的Reflection,協助更輕易地實作「Simple Factory Method」,並且提供更佳的擴充性。
Rreflection必須由程式語言提供支援,許多現今流行的程式語言,例如Java、C#、Ruby,都提供Reflection的機制。透過Reflection機制,最明顯的效果便是,系統的行為可以等到執行期才決定,而非編譯時期即已決定。簡單的說,Reflection和動態型別的載入及運用有關。
在Java中,有個名為Class的類別,用來代表Java中所有被載入的類別,所以它是個類別的「類別」。Class類別提供一個名為forName()的method,可以傳入類別的名稱,而由它回傳該類別名稱相對應的Class物件,例如:
便能夠取得代表LDAPAuthenticator這個類別的Class物件。Class物件提供一個名為newInstance()的method,能夠產生一個新的、隸屬於該Class的物件。結合以上所提到的兩點,你會猛然發現,其實並不需要在程式中指定究竟要產生那一種類別的物件,但仍舊能夠產生。例如:
相較於使用new語法,過去在程式中明確的指定欲產生物件的類別名稱:
使用動態類別物件產生方式的前者,並沒有寫明要產生的類別名稱,而是由讀取系統組態設定的方式,由外部讀取要產生的類別名稱,程式碼本身完全和要產生的類別一點關聯都沒有,就算想要改變被產生的類別,也不會影響到這段程式碼。
反觀後者,程式碼本身「寫死」了要產生的類別名稱(上例中的LDAPAuthenticator),所以當你想要改變這件事的時候,就必須修改程式碼。
運用Reflection機制動態產生物件的方式,可以寫成新版的「Simple Factory Method」:
我們捨棄了一長串的if-else-if的寫法,改用了動態生成物件的方式。在利用newInstance()產生出物件後,進一步檢查該物件是否為Authenticator的實作,倘若不是,直接回傳null,否則便進一步將該物件轉型為Authenticator。
在改用這個寫法之後,可以得到一些好處。首先,程式碼不再寫死認證類別的名稱,所以當你要改變認證類別時,並不會影響到這段程式碼(也就是說,毋需再加重新編譯,只需要更動系統組態設定即可)。
再來,這段程式碼並不知道究竟會需要支援多少種認證類別,它只在意,被設在authClassName中的類別,是否實作了Authenticator介面。也許你的系統今天在第一位客戶設定的authClassName是Customer1Authenticator,但明天這套系統在第二位客戶端安裝時,要採用的authClassName卻是Customer2Authenticator(因為不同的客戶需要不同的客製認證方式)。這樣的改變,對系統來說,卻只需要將Customer2Authenticator直接加到程式路徑中,並且設定authClassName為Customer2Authenticator,毋需更動程式碼。
這意謂著你可以隨意地「掛上」所需的認證類別,雖然「Simple Factory Method」對它們一無所知,卻又無礙於它產生這些認證類別的物件。系統的擴充性因此大為改善。
透過這樣的方式,我們鬆綁了工廠與產品之間的關聯性,製造產品的工廠本身,與它所製造的產品之間的相依性又降得更低。這使得需求的更動對系統造成的衝擊又變得更少。
《作者簡介》王建興
清華大學資訊工程系的博士研究生,研究興趣包括電腦網路、點對點網路、分散式網路管理、以及行動式代理人,專長則是Internet應用系統的開發。曾參與過的開發專案性質十分廣泛而且不同,從ERP、PC Game到P2P網路電話都在他的涉獵範圍之內。
相關閱讀:
探索產生物件的技巧(1)當心隨手New一下引發的衝擊效應
探索產生物件的技巧(2)生成模式的初階應用
探索產生物件的技巧(4)抽象可抵擋變動引發的星星之火
探索產生物件的技巧(5)維持類別僅存在一份實例的設計方法
探索產生物件的技巧(6)受夠重新開機?試試自行管理記憶體
探索產生物件的技巧(7)避免時效性不高的重複性動作
探索產生物件的技巧(終)間接產生物件的訴求:降低相依性
熱門新聞
2025-01-26
2025-01-25
2025-01-26
2025-01-27
2024-04-24
2025-01-24