1. 程式人生 > 其它 >4.建立型模式(單例&原型)

4.建立型模式(單例&原型)

1.建立型模式概述

1.1設計模式的分類簡述

  • 設計模式有兩種分類方式: 1.模式目的 2.模式的作用範圍
    • 根據目的來分:即根據模式是用來完成什麼工作來劃分,可分為如下三種
      1. 建立型模式
      2. 結構型模式
      3. 行為型模式
    • 根據作用範圍來分:即根據模式主要用於類上還是用於物件上來分,可分為如下兩種
      1. 類模式:處理類和子類之間的關係,通過繼承來建立,靜態(編譯時便確定下來)。
      • 類模式範疇:工廠方法,類介面卡,模板方法,直譯器
      1. 物件模式:處理物件之間的關係,這些關係可通過組合或聚合來實現,執行時是可變化的,動態
      • 物件模式:除了類模式的四種方法其他都是物件模式

1.2 建立型模式簡述

  • 關注點:描述“如何建立物件”
  • 特點:將物件的建立和使用分離(降低系統耦合度)
  • 主要有如下五類建立型模式
    1. 單例模式
    2. 原型模式
    3. 工廠方法
    4. 抽象工廠
    5. 建造者模式

2.單例模式

2.1 單例模式概述

  • 定義:一個類只有一個例項,且該類能自行建立這個例項的一種模式。
  • 特點:單例類只有一個由該單例類自行建立的例項物件,對外提供一個訪問該單例的全域性訪問點。
  • 應用場景
    1. 需要頻繁建立的一些類,使用單例可以降低系統的記憶體壓力,減少 GC。
    2. 某類只要求生成一個物件的時候,如一個班中的班長、每個人的身份證號等。
    3. 某些類建立例項時佔用資源較多,或例項化耗時較長,且經常使用。
    4. 某類需要頻繁例項化,而建立的物件又頻繁被銷燬的時候,如多執行緒的執行緒池、網路連線池等。
    5. 頻繁訪問資料庫或檔案的物件。
    6. 對於一些控制硬體級別的操作,或者從系統上來講應當是單一控制邏輯的操作,如果有多個例項,則系統會完全亂套。
    7. 當物件需要被共享的場合。由於單例模式只允許建立一個物件,共享該物件可以節省記憶體,並加快物件訪問速度。如 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.原型模式

  • 定義: 用一個已經建立的例項作為原型,通過複製該原型物件來建立一個和原型相同或相似的新物件
  • 結構:抽象原型類、具體原型類、訪問類
    1. 抽象原型類:規定了具體原型物件必須實現的介面
    2. 具體原型類:實現了抽象原型類的clone()方法,為可被複制的物件
    3. 訪問類:使用具體原型類的clone()方法來複制新的物件

優點:
1.Java 自帶的原型模式基於記憶體二進位制流的複製,在效能上比直接 new 一個物件更加優良。
2.可以使用深克隆方式儲存物件的狀態,使用原型模式將物件複製一份,並將其狀態儲存起來,簡化了建立物件的過程,以便在需要的時候使用(例如恢復到歷史某一狀態),可輔助實現撤銷操作。

缺點:
1.需要為每一個類都配置一個 clone 方法
2.clone 方法位於類的內部,當對已有類進行改造的時候,需要修改程式碼,違背了開閉原則。
3.當實現深克隆時,需要編寫較為複雜的程式碼,而且當物件之間存在多重巢狀引用時,為了實現深克隆,每一層物件對應的類都必須支援深克隆,實現起來會比較麻煩。因此,深克隆、淺克隆需要運用得當。

2.1 原型模式實現

  1. 淺克隆:建立一個新物件,新物件的屬性和原來物件完全相同,且對於非基本型別屬性其仍指向原有屬性所指向的物件記憶體地址
  2. 深克隆:建立一個新物件,屬性中引用的其他物件也會被克隆而不再指向原有物件地址。
  • 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方法獲取複製的原型。

具體示例:用帶原型管理器的原型模式來生成包含“圓”和“正方形”等圖形的原型,並計算其面積。分析:本例項中由於存在不同的圖形類,例如,“圓”和“正方形”,它們計算面積的方法不一樣,所以需要用一個原型管理器來管理它們。