? ? 在23種經典設計模式中,最開始的設計模式——工廠方法模式,抽象工廠模式和未被歸納到設計模式中但經常被OOP開發所采用的簡單工廠方法模式之間有著巨大的共性,和具體應用層面的區別。
????本文試圖把這些工廠類設計模式對開發有影響的共性和區別通過比較的方式闡述清楚,幫助讀者掌握這三種設計模式的應用。
????開閉原則
在甄別這幾種設計模式前我們先理解一個原則:開閉原則
“軟件中的對象應該對于擴展是開放的,但是對于修改是封閉的”????——維基定義
????開閉原則是五大設計原則(SOLID)中最為抽象的一個,因此它的描述也極為簡單易懂。它的目的單一,就是為了迎合改變(ready for change)。滿足這個原則的程序在出現需求更改,維護變動的時候可以將代碼改變量減小到最小,從而很容易地實現產品的升級迭代,大大增強軟件的“生命力”。
? ? 了解開閉原則以及目的后我們看看簡單工廠方法。
?????簡單工廠方法
? ? 簡單工廠方法也稱靜態工廠方法,其實質就是在用戶直接調用所需應用類構造器生成應用類的過程中加一堵墻,使得用戶與應用類分離開,耦合度下降。
? ? Track track = new ConcreteTrack();
????變成了
? ??Track track = new TrackFactory().createTrack();//返回的是ConcreteTrack實例
? ? 這樣用戶將獲取具體應用類的過程變成了獲取工廠,調用工廠方法的過程。用戶與具體應用類的直接聯系就被工廠這堵墻隔絕了,減少了具體類的暴露,從而客戶不會對具體類進行過度依賴,開發者日后修改具體類也就不會對濫用具體應用類的客戶端造成致命打擊。這是防暴露層面簡單工廠的好處,而這一好處是所有工廠方法所共有的。
? ? 順便問一下,簡單工廠為什么也叫靜態工廠呢?
? ? 直觀解釋往往是”因為簡單工廠提供的生成產品的方法是靜態的“。但是實際上這不是必要的,你也可以把這個生成產品的方法寫成非靜態方法,所以不要誤認為簡單工廠生成產品的方法都是靜態的。
????我們接下來稱工廠生成產品的方法為產品方法。那么簡單工廠或靜態工廠方法的核心就是其產品方法的“單一”屬性。
? ? 這個單一有兩個層面的解釋。
? ? 第一,方法數量的單一。往往一個靜態工廠中只有一個產品方法。但是問題是,這個工廠卻需要具備生成多個產品的能力,因此開發者需要把生成多個產品的邏輯全塞在這一個方法中。于是,開發者就無法通過繼承等OOP手段讓這個工廠的設計符合開閉原則,因為在擴展升級中,你不能通過繼承去擴充父類方法的具體邏輯,你只能直接修改父類產品方法的邏輯去實現擴充。所以采取純靜態工廠方法,你就面臨每次增加新產品需求就必須修改原工廠產品方法代碼的窘境。然而修改產品方法的邏輯顯然違背對修改封閉的原則。
????所以靜態方法的致命缺陷就顯露了出來,它是這三個設計模式中唯一不滿足開閉原則的設計模式,它不具備很好的后期擴展性。但是由于它是最簡單的工廠類設計模式,它直白地展示出工廠類設計模式的優點(隔離客戶與具體類),并且容易被理解應用,實現代價也小,所以在產品種類少,變化考慮非首要的場景下它經常被使用。
? ? 第二,架構層次單一。靜態方法不需要人們為它特意準備一個接口,它可以直接根據開發者的需要去創建具體類,所以在接口,抽象類,實現類三個架構層次中它只需要實現類這一個層次。這樣,采用簡單工廠方法設計的整體架構就變的很簡潔。
? ? 工廠方法模式
? ? 工廠方法可以理解為靜態方法的進化,具體來講就是“單一”屬性進化成“多個”。
? ? 在架構層次上我們擴充工廠的接口層,并把重點放到接口層而非實現類。我們定義一個工廠接口,其中規定好產品方法的signature,用產品的統一接口來指定返回值的類型。這樣工廠就具備了至少兩個層次,工廠接口層次和具體工廠實現類層次。
? ? 說到這里提一下產品的架構層次,很簡單,同一類產品應該實現同一個接口(如汽車類產品應實現汽車接口),所以產品是有接口層的,又抽象類層不是必要的,所以產品也至少有兩個層次。
? ??這里我們規定實現同一個接口的產品的集合被稱為一個產品等級結構,為后面介紹抽象工廠做準備。
? ? 說回工廠方法的架構層次數,它相對靜態共產方法就是“多個”。而這樣做帶來了巨大的好處,我們可以通過實現接口來實現對新工廠和對應產品的拓展,完美地滿足了開閉原則。每當我們需要擴展新的產品時,我們不再是對一個具體實現類添加生成產品的邏輯,而是新建一個實現類,在里面實現產品方法。關閉對已有代碼的修改,而打開了實現接口進行拓展的大門。
? ? 另一個層面上,在實際開發中隨著產品種類的增加,工廠實現類的數量也增加了,從單一變成多個。而這也就引出了工廠方法的缺點,隨著產品種類的數量擴充,工廠實現類的數量也一同擴充,工廠類的數量有時會發生暴漲,帶來巨大的代碼代價。因此,實際開發中可以采取靜態工廠方法與工廠方法結合的策略減少過多的工廠類。
? ? 抽象工廠方法
? ? 抽象工廠方法又可以理解成是對工廠方法的進化,原則也可以理解成是“單一”屬性變成“多個”。
? ? 那是什么屬性從單一變成了多個了呢?
? ? 一. 產品等級結構單一變成了多個。
? ? 簡單的說就是開發產品的種類變多了,以前只要造汽車的引擎,現在連汽車的玻璃,輪子也要一起造。
? ? 這里我們再定義產品族的概念:搭配起來一起使用的不同類產品(來自不同產品等級結構的產品)的集合理解為一個產品族。
例子:造一個行星系統,那么恒星和行星就是不同類的產品,太陽和地球就是這兩類產品的具體類,如果我們要造太陽系,那么這兩個產品就會被搭配起來使用,這時太陽和地球就變成了一個產品族。
? ? 二. 工廠的產品方法從單一變成了多個。
? ? 無論是接口還是具體工廠實例,同一個工廠不再受限制于生成單一的產品,它開始具備生成一整個產品族(多個不同類產品)的能力。
例子:我們要造一個行星系統,我們調用“宇宙工廠”的產品方法生成恒星,行星,軌道,這三種產品。再造原子系統,調用“原子工廠”又會生成原子核,電子,能量軌道。而宇宙工廠和原子工廠是實現同一個接口的工廠實現類,他們分別滿足我們對不同場景對多個不同產品的需求。
? ? 抽象工廠是實際應用中最廣泛的工廠類方法,因為它考慮的情況最復雜最接近實際開發。它是在工廠方法上進化過來的,它通過把工廠方法集成帶來便利的同時卻也帶來了弊端。
? ? 開閉原則的傾斜性
????“開閉原則”要求系統對擴展開放,對修改封閉,通過擴展達到增強其功能的目的。對于涉及到多個產品族與多個產品等級結構的系統,其功能增強包括兩方面:
? ? 1. 增加產品族:對于增加新的產品族,工廠方法模式很好的支持了“開閉原則”,對于新增加的產品族,只需要對應增加一個新的具體工廠即可,對已有代碼無須做任何修改。
? ? 2. 增加新的產品等級結構:對于增加新的產品等級結構,需要修改所有的工廠角色,包括抽象工廠類,在所有的工廠類中都需要增加生產新產品的方法,不能很好地支持“開閉原則”。
????抽象工廠模式的這種性質稱為“開閉原則”的傾斜性,抽象工廠模式以一種傾斜的方式支持增加新的產品,它為新產品族的增加提供方便,但不能為新的產品等級結構的增加提供這樣的方便。所以當出現新的產品等級結構需求時,它就變的和靜態工廠方法一樣無法適應開閉原則。
? ? 總結
? ? 簡單工廠方法,工廠方法,抽象工廠是一個層層進化的過程。每次進化都是前一個設計模式中某個“單一”層面變成“多個”從而將考慮情況變的復雜,適用場景變的廣泛。
? ? 簡單工廠方法將所有產品生成的邏輯塞在同一個產品方法中,不適合大規模變化拓展,但是實現簡單。
? ? 工廠方法通過實現接口來拓展新的工廠,提供新的產品方法,滿足開閉原則。它適合不確定的產品類的生成,但是容易因產品過多而產生繁瑣的工廠類和代碼。
? ? 抽象工廠方法把工廠的產品方法變多了,這樣一個工廠可以產生多個產品,產品成體系地被工廠產生。適用于復雜的產品需求環境,同時又因為開閉原則傾斜性,產品的大類不適合拓展。
????在實際開發中切記他們適用的開發場景,以減小他們的弊端,提高價值。面對復雜的場景,應試圖在這些模式上組合拓展,而不是死板地套用單個設計模式。