4.建立型模式(單例&原型)
阿新 • • 發佈:2021-10-10
1.建立型模式概述
1.1設計模式的分類簡述
- 設計模式有兩種分類方式: 1.模式目的 2.模式的作用範圍
- 根據目的來分:即根據模式是用來完成什麼工作來劃分,可分為如下三種
- 建立型模式
- 結構型模式
- 行為型模式
- 根據作用範圍來分:即根據模式主要用於類上還是用於物件上來分,可分為如下兩種
- 類模式:處理類和子類之間的關係,通過繼承來建立,靜態(編譯時便確定下來)。
- 類模式範疇:工廠方法,類介面卡,模板方法,直譯器
- 物件模式:處理物件之間的關係,這些關係可通過組合或聚合來實現,執行時是可變化的,動態
- 物件模式:除了類模式的四種方法其他都是物件模式
- 根據目的來分:即根據模式是用來完成什麼工作來劃分,可分為如下三種
1.2 建立型模式簡述
- 關注點:描述“如何建立物件”
- 特點:將物件的建立和使用分離(降低系統耦合度)
- 主要有如下五類建立型模式
- 單例模式
- 原型模式
- 工廠方法
- 抽象工廠
- 建造者模式
2.單例模式
2.1 單例模式概述
- 定義:一個類只有一個例項,且該類能自行建立這個例項的一種模式。
- 特點:單例類只有一個由該單例類自行建立的例項物件,對外提供一個訪問該單例的全域性訪問點。
- 應用場景
- 需要頻繁建立的一些類,使用單例可以降低系統的記憶體壓力,減少 GC。
- 某類只要求生成一個物件的時候,如一個班中的班長、每個人的身份證號等。
- 某些類建立例項時佔用資源較多,或例項化耗時較長,且經常使用。
- 某類需要頻繁例項化,而建立的物件又頻繁被銷燬的時候,如多執行緒的執行緒池、網路連線池等。
- 頻繁訪問資料庫或檔案的物件。
- 對於一些控制硬體級別的操作,或者從系統上來講應當是單一控制邏輯的操作,如果有多個例項,則系統會完全亂套。
- 當物件需要被共享的場合。由於單例模式只允許建立一個物件,共享該物件可以節省記憶體,並加快物件訪問速度。如 Web 中的配置物件、資料庫的連線池等。
優點:
1.單例模式保證記憶體中只有一個例項,減少記憶體開銷
2.避免對資源多重佔用
3.可設定全域性訪問點,優化和共享資源的訪問
缺點:
1.一般沒有介面,擴充套件困難。違背開閉原則
2.併發測試中,單例模式不利於程式碼除錯。且除錯過程中若單例中的程式碼沒有執行完,也不能模擬生成一個新得物件。
3.功能程式碼常寫在一個類中,易違背單一職責原則。
2.2 結構與實現
- 私有化建構函式,靜態化例項屬性,向外提供一個靜態公有函式用於建立/獲取靜態私有例項。
實現方式1-懶漢式單例
- 特點:類載入時沒有生成單例,第一次呼叫getInstance方法時建立單例
- volatile,synchronized保證執行緒安全,但每次訪問時都要同步-影響效能且消耗更多資源
實現示例:
public class SingletonLazy {
private static volatile SingletonLazy instance = null;
//私有構造方法
private SingletonLazy(){}
public static synchronized SingletonLazy getInstance() {
if (instance == null) {
instance = new SingletonLazy();
}
return instance;
}
}
實現方式2-餓漢式單例
- 特點:類載入時就建立一個單例,保證在呼叫getInstance前單例已存在
- 類建立時就已建立好一個靜態物件供系統使用且以後不再改變-執行緒安全
實現示例:
class SingletonHungry {
private static final SingletonHungry instance = new SingletonHungry();
private SingletonHungry(){}
public static SingletonHungry getInstance() {
return instance;
}
}
擴充套件-有限多例
- 將私有靜態單例改為私有靜態例項列表
實現示例:
class SingletonHungryList {
private static final ArrayList<SingletonHungryList> list = new ArrayList<>();
static {
for (int i = 0; i < 10; i++) {
list.add(new SingletonHungryList());
}
}
private SingletonHungryList(){}
public static SingletonHungryList getInstance() {
int i = (int) Math.random() * 10;
return list.get(i);
}
}
2.原型模式
- 定義: 用一個已經建立的例項作為原型,通過複製該原型物件來建立一個和原型相同或相似的新物件。
- 結構:抽象原型類、具體原型類、訪問類
- 抽象原型類:規定了具體原型物件必須實現的介面
- 具體原型類:實現了抽象原型類的clone()方法,為可被複制的物件
- 訪問類:使用具體原型類的clone()方法來複制新的物件
優點:
1.Java 自帶的原型模式基於記憶體二進位制流的複製,在效能上比直接 new 一個物件更加優良。
2.可以使用深克隆方式儲存物件的狀態,使用原型模式將物件複製一份,並將其狀態儲存起來,簡化了建立物件的過程,以便在需要的時候使用(例如恢復到歷史某一狀態),可輔助實現撤銷操作。
缺點:
1.需要為每一個類都配置一個 clone 方法
2.clone 方法位於類的內部,當對已有類進行改造的時候,需要修改程式碼,違背了開閉原則。
3.當實現深克隆時,需要編寫較為複雜的程式碼,而且當物件之間存在多重巢狀引用時,為了實現深克隆,每一層物件對應的類都必須支援深克隆,實現起來會比較麻煩。因此,深克隆、淺克隆需要運用得當。
2.1 原型模式實現
- 淺克隆:建立一個新物件,新物件的屬性和原來物件完全相同,且對於非基本型別屬性其仍指向原有屬性所指向的物件記憶體地址。
- 深克隆:建立一個新物件,屬性中引用的其他物件也會被克隆而不再指向原有物件地址。
- Java中的Object類提供了淺克隆的clone()方法,具體原型類只要實現Cloneable介面就可實現物件的淺克隆(cloneable介面就是抽象原型類)
具體實現(ShallowCopy為具體原型類):
public class Prototype {
public static void main(String[] args) throws CloneNotSupportedException {
ShallowCopy s1 = new ShallowCopy();
ShallowCopy s2 = s1.clone();
System.out.println("s1與s2記憶體地址相同嗎? " + (s1 == s2));
System.out.println("s1與s2的o屬性地址相同嗎? " + (s1.o == s2.o));
}
}
class ShallowCopy implements Cloneable {
public InCopy inCopy = new InCopy();
public ShallowCopy clone() throws CloneNotSupportedException {
System.out.println("已複製原型物件");
return (ShallowCopy) super.clone();
}
}
class InCopy implements Cloneable {
/* public InCopy clone() throws CloneNotSupportedException {
System.out.println("已複製原型物件");
return (InCopy) super.clone();
}*/
}
/*
已複製原型物件(未註解inCopy內的InCopy時,若註解則s1與s2的inCopy屬性地址相同為false)
s1與s2記憶體地址相同嗎? false
s1與s2的inCopy屬性地址相同嗎? true
*/
擴充套件-原型管理器原型模式
- 在原型基礎上增加一帶原型管理器的PrototypeManager類,用HashMap儲存多個複製的原型,Client類可通過管理器的get方法獲取複製的原型。
具體示例:用帶原型管理器的原型模式來生成包含“圓”和“正方形”等圖形的原型,並計算其面積。分析:本例項中由於存在不同的圖形類,例如,“圓”和“正方形”,它們計算面積的方法不一樣,所以需要用一個原型管理器來管理它們。