創建型模式關注對象的創建過程,它將對象的創建和使用分離,在使用對象時無須知道對象的創建細節,使整個系統的設計更加符合單一職責原則
與之相關的模式有:
- 簡單工廠模式(思想重要,但不屬於GoF23種設計模式)
- 工廠方法模式
- 抽象工廠模式
- 建造者模式
- 原型模式
- 單例模式
1. 簡單工廠模式(Simple Factory)
1.1 定義
又稱靜態工廠方法(Static Factory Method)模式,屬於類創建型模式。
該模式中,專門定義一個工廠類,根據參數的不同來負責創建其他類的實例。
目的:
為了分離對象的創建與使用而生。
1.2模式結構
省略部分關系箭頭,僅保留繼承,為了看着清晰
該模式下包含如下角色:
-
Factory(工廠角色)
提供靜態工廠方法,負責創建其他類實例,它返回的是抽象層的產品類,所有具體產品都是抽象產品的子類。 -
Product(抽象產品角色)
描述產品實例所具有的接口方法,“客戶使用”以及“工廠返回”都是該抽象產品類型 -
ConcreteProduct(具體產品角色)
繼承自抽象產品,實現接口方法
1.3 優缺點
優點:
- 客戶無需知道對象具體構建細節,將創建與使用分離,對客戶更加友好,系統更符合單一職責原則。
- 因為第1點的分離,若對象構建邏輯有所改變時,只需修改工廠邏輯即可,不用修改所有客戶類。相較於以往,在一定程度上,在“開閉性”上稍有改善。
- 抽象產品的引入(雖不一定是模式強制引入的),使系統更加符合依賴倒轉原則。
缺點:
- 該單一工廠任務過重,系統擴展困難,一旦需要添加新產品,則不得不修改工廠邏輯,整個系統都可能受影響,不利於擴展和維護,不符合開閉原則。
- 增加了類的個數,從某方面來講增加了系統復雜度。
2. 工廠方法模式(Factory Method)
2.1 定義
"Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses."
也稱工廠模式、虛擬構造器模式(Virtual Constructor),或多態工廠模式(Polymorphic Factory),屬於類創造型模式。
該模式,抽象工廠父類定義構建產品對象的公共接口,子工廠則負責創建具體產品對象
目的:
為了彌補簡單工廠模式的不足之處,解決單一工廠職責過重問題,使代碼解耦,符合開閉原則。
簡單理解:繼承了簡單工廠模式優點,彌補了缺點。引入抽象工廠層,具體單個工廠只生產單一產品,客戶面對抽象、接口編程。
2.2 模式結構
-
Product(抽象產品)
含義同簡單工廠模式 -
ConcreteProduct(具體產品)
某類型具體產品由專門的具體工廠創建,一一對應 -
Factory(抽象工廠)
聲明了工廠方法(Factory Method)接口,該方法用於返回產品。 -
ConcreteFactory(具體工廠)
繼承或實現抽象工廠,由客戶調用,構建返回某一種具體產品類實例。
2.3 優缺點
優點:
繼承了簡單工廠模式的優點,並彌補了其缺點。因為抽象工廠層的引入,以及對每種產品生產工廠的分割,使得在增改產品時,只需要添加或改動對應工廠即可,其他代碼不需要做任何修改,使得系統更加依賴倒轉與開閉原則。
注:需通過反射,再配合xml配置文件或IOC來達成,即客戶端不直接new具體工廠,而寫入配置中,可以在一定程度上解耦
缺點:
每類新產品都需要對應工廠,導致類個數更多增加,某方面來看增加系統復雜度。
3.抽象工廠模式(Abstract Factory Pattern)
3.1 定義
"Provide an interface for creating families of related or dependent object without specifying their concrete classes."
提供一個創建一系列相關或相互依賴對象的接口,而無須指定它們具體的類。
又稱 Kit模式,屬於對象創建型模式。
目的:
當存在多個產品等級結構時,傳統的工廠方法模式的工廠類數量將大量增加。為了解決此問題而生
簡單理解:工廠方法模式只是該模式的特例。該模式中,工廠抽象層不只是定義一個生產單類產品的接口方法,而是用於生產產品族的一組接口方法,比如具體工廠類海爾工廠則實現3個方法,分別生產海爾電視機,海爾冰箱,海爾空調。
相關概念:
- 產品等級結構:即單類產品的繼承結構。比如一個抽象類是空調,其具體子類為海爾空調,格力空調...,則該抽象空調與每類具體品牌空調之間構成了一個產品等級結構。
- 產品簇:在抽象工廠模式中,產品簇是指由同一工廠生產的,位於不同產品等級結構的一組產品。如海爾空調,海爾電視機...
3.2 模式結構
- AbstractFactory(抽象工廠)
定義一組用於生成抽象產品對象的方法,每一個方法對應一個產品等級結構 - ConcreteFactory(具體工廠)
- AbstractProduct(抽象產品)
- ConcreteProduct(具體產品)
3.3 優缺點
優點:
- 相較於工廠方法模式,減少了類的個數。
- 當一個產品族種多個對象被設計成一起工作時,它能保證客戶端始終只使用同一個產品族中的對象。這對一些需要根據當前環境來決定其行為的軟件系統來說,是一種非常使用的設計模式。
缺點:
因為多產品構建方法糅合到了每個工廠中,導致一定程度上增加了耦合性,導致了開閉原則的“傾斜性”,即若要增加一個產品族,則只需添加一個具體工廠,無須修改其他任何代碼,符合開閉原則;但若想增加一種新產品(產品等級結構),則要添加新接口方法,這將涉及到對抽象工廠及其所有子類的修改,則又不符合開閉原則。
4. 建造者模式(Builder Pattern)
4.1 定義
"Separate the construction of a complex object from its representation so that the same construction process can create different representations."
將復雜對象的構建與它的表示分離,以至於相同的構建過程可以創建不同的表示
又稱生成器模式,屬於對象創建型模式。
目的:
為了解決復雜對象的構建問題。將復雜對象構建步驟分解,並隔離復雜對象創建與使用(或許會想着似乎與工廠相似,但實際有區別的,參照下述理解)
簡單理解:
對於該模式完整含義,可以通過工廠方法模式來理解,工廠方法模式關注的是單類產品多個不同種類的構建,即不同的具體工廠返回不同種類的產品。而建造者模式,則是關注單個復雜對象的構建,抽象建造者定義"設置產品各個部分或屬性"的接口,而不同的具體建造者通過對相同接口有着不同的實現邏輯,達到對於同種產品有着不同的構建內容。而指揮者則負責安排這些各個部分或屬性接口的調用次序,構建並返回對象給客戶。
達到隔離復雜對象的創建和使用,使得相同創建過程可以創建不同產品(比如,面向抽象編程,再將具體Builder的選擇配置到xml文件中獲取)
若對象各部分(即那些設置屬性的接口方法)構建順序不重要,則完全可以把指揮者合並到建造者里面去,提供一個方法以固定順序構建和返回對象。同理,若系統只需一個具體建造者的話,則也可以省略抽象建造者。
在實際使用中:
Director角色往往被省略,Builder也作為靜態內部類被放入產品內部(使用靜態的原因,是可以不依賴於外部類實例而被創建),通過使用Builder實例中方法的鏈式調用,來構建產品對象。使得整體更加簡單靈活。
public class ProductObj {
private final String mName;
private final String mValue;
private ProductObj(Builder builder) {
this.mName = builder.name;
this.mValue = builder.value;
}
public static class Builder {
private String name;
private String value;
public Builder buildName(String nameObj) {
this.name = nameObj;
return this;
}
public Builder buildValue(String valueObj) {
this.value = valueObj;
return this;
}
public ProductObj build() {
return new ProductObj(this);
}
}
}
4.2 模式結構
以下各角色具體含義,在上文“簡單理解”中已提到,簡單說一下
-
Builder(抽象建造者)
定義"設置產品各個部分或屬性"的接口,且可以定義一個產品變量並初始化產品實例,還可以再定義一個返回產品的方法 -
ConcreteBuilder(具體建造者)
繼承抽象建造者,不同的具體建造者根據自己業務需求實現上述接口 -
Product(產品角色)
復雜對象 -
Director(指揮者)
(若接口調用順序不重要則可省略)由客戶端創建,並由傳入的具體建造者,根據一定順序調用其“設置對象屬性”的接口方法,來設置對象,最后返回對象。
4.3 優缺點
- 將復雜產品的創建步驟分解在不同方法中,使得創建過程更加清晰,方便控制。
- 在原完整定義建造者模式下,不同具體建造者間相互獨立,若想增減時無須修改其他代碼,符合開閉原則。
該模式適用於那些擁有復雜內部結構的產品,且在簡化的情況下,也使得客戶也更能清晰對對象的每一塊進行設置。
5. 原型模式(Prototype Pattern)
5.1 定義
"Specify the kind of objects to create using a prototypical instance, and create new objects by copying this prototype."
用原型實例來確定要創建對象的類型,並通過復制原型來創建新的對象。
屬於對象創建型模式。
目的:
為了簡化多次創建某一類型對象的過程,或保存對象中間狀態。
簡單理解:在原型類中實現克隆方法(從克隆接口或者說抽象原型類繼承的),客戶則需要通過new或者其他方式創建一個原型對象,然后調用克隆方法即可復制得到多個相同對象。
相關概念:
- Java對原型模式提供了完美支持,所有類的父類Object中提供並實現了clone()方法(相當於抽象原型類),只需在在子類中將其重寫為public或protected方法,再在其中調用super.clone()即可(因為原方法是protected修飾,只有重寫才能被lang包以外,以及"繼承於Object的具體原型類"以外調用【具體參考java protected修飾知識】),並實現Cloneable接口(標識接口,無具體方法,否則調clone會報異常)即可。注:clone()方法為淺克隆
- 淺克隆:克隆對象與原對象本身不是同一個對象,但其中的對象成員變量仍指向相同的值。
- 深克隆:把要復制的對象,以及其所引用的對象都復制了一邊。
5.2 模式結構
圖為“帶原型管理器的原型模式”(因為一般原型模式就一個clone方法,沒什么好表示的)
-
Prototype(抽象原型類)
定義了克隆自己的方法接口,可以是抽象類或接口。 -
ConcretePrototype(具體原型類)
實現了克隆方法,用以復制自身返回一個克隆對象。 -
Client(客戶類)
個人感覺該角色沒必要,只要使用的地方都可以算客戶,沒使用也不影響“我”是可克隆的。書上定義的 -
(可選)Prototype Manager(原型管理器)
即原型類實例的獲取不再由客戶主動new或者手動調用clone(),而是由原型管理器統一管理。管理器在初始化時,則實例化所有具體原型類,並存入一個集合當中(比如鍵值對的形式)。當客戶需要獲取某個對象克隆時,則調用管理器的獲取方法,該方法根據傳入參數,克隆對應原型對象並返回。
5.3 優缺點
當需創建對象較為復雜時,或大量重復創建某對象時,可簡化創建過程,提高新實例創建效率。也可適用某些需要保存對象狀態的場合。個人看來無明顯缺點,只是一種模式,需要的時候用即可。(教材上非要與工廠模式相比,說簡化創建結構。個人感覺有些牽強,針對目標使用場景都不同)
6. 單例模式(Singleton Pattern)
6.1 定義
"Ensure a class has only one instance and provide a global point of access to it"
確保一個類只擁有一個實例,且能提供全局訪問的方法。
又稱單件模式或單態模式,屬於對象創建型模式。
目的:
當希望某個類在系統中的實例只存在一個時,可使用該模式
6.2 模式結構
public class LazySingleton { //懶漢式單例模式
private static LazySingleton instance = null;
private LazySingleton(){
}
synchronized public static LazySingleton getInstance(){
if(instance == null)
instance = new LazySingleton();
return instance;
}
}
該模式只存在一個角色,即單例類本身。單例類中將構造函數私有化,防止外部調用。且定義一個自身靜態成員變量用於存儲實例,然后提供一個靜態工廠方法供外部獲取。而具體自行實例化時機,則分為 餓漢式單例類 以及懶漢式單例類。
相關介紹:
- 餓漢式單例類,
private static EagerSingleton instance = new EagerSingleton();
即,類加載時便實例化自身。 - 懶漢式單例類,將實例化延遲到工廠方法被調用時。【注:必須處理好多個線程同時首次引用訪問該方法的問題,因此需要通過同步化機制進行控制】
6.3 優缺點
將構造函數私有而自行實例化,確保了類實例的唯一性,且可對訪問進行嚴格控制。但因為同時負責創建和業務使用,所以單例類職責過重。