Java程序員必備知識——常用設計模式:單列+裝飾器+策略+觀察者

Java程序員必備知識——常用設計模式:單列+裝飾器+策略+觀察者

一、單列模式

1、懶漢(線程不安全,懶加載):如果沒有實例就創建並返回,會有線程安全問題,簡單加synchronized解決,但效率不高,升級版本是DCL。

public class Singleton {
private static Singleton instance;
private Singleton (){
}
public static Singleton getInstance() {
if (instance null) {
instance = new Singleton();
}
return instance;
}
}

2、懶漢的升級DCL【雙重檢索】(線程安全,懶加載):加兩個if是因為可能會有多個線程一起進入同步塊外的if,如果在同步塊內不進行二次檢驗的話就可能會生成多個實例;instance = new Singleton()這句會有重排序的問題,用volidate解決。

public static Singleton getSingleton() {
if (instance null) {
//Single Checked
synchronized (Singleton.class) {
if (instance null) {
//Double Checked
instance = new Singleton();
//有JVM重排序問題

}
}
}
return instance ;
}
//防止JVM指令重排序
public class Singleton {
private volatile static Singleton instance;
//聲明成 volatile,禁用JVM指令重排序
private Singleton (){
}
public static Singleton getSingleton() {
if (instance null) {
synchronized (Singleton.class) {
if (instance null) {
instance = new Singleton();
}
}
}
return instance;
}
}

3、餓漢(線程安全,非懶加載):利用類加載器保證線程安全,但不是懶加載。

public class Singleton{
//類加載時就初始化,無法從外部文件設置屬性,如從properties文件設置屬性
private static final Singleton instance = new Singleton();
private Singleton(){
}
public static Singleton getInstance(){
return instance;
}
}

4、餓漢升級->靜態內部類(線程安全,懶加載):在餓漢的基礎上使用靜態內部類解決非懶加載問題。

public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){
}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}

5、枚舉(線程安全,非懶加載):寫法最簡單,可以防止反序列化,但不是懶加載。

public enum EasySingleton{
INSTANCE;
}

總結:一般情況下直接使用餓漢式就好了,如果明確要求要懶加載會傾向於使用靜態內部類,如果涉及到反序列化創建對象時會試著使用枚舉的方式來實現單例。

Java程序員必備知識——常用設計模式:單列+裝飾器+策略+觀察者

二、裝飾器模式

在保持原有功能接口不變的基礎上動態擴展功能的模式。 ​ Java IO包中就使用了該模式,InputStream有太多的實現類如FileInputStream,如果要在每個實現類上加上幾種功能如緩衝區讀寫功能Buffered,則會導致出現FileInputStreamBuffered, StringInputStreamBuffered等等,如果還要加個按行讀寫的功能,類會更多,代碼重複度也太高,你說改原來的接口也行啊,但是這樣就是改變接口的內容了,現在我想做到不更改以前的功能,動態地增強原有接口。 所以使用FilterInputStream這個抽象裝飾器來裝飾InputStream,使得我們可以用BufferedInputStream來包裝FileInputStream得到特定增強版InputStream,且增加裝飾器種類也會更加靈活。

缺點:會引入很多小類,從而增加使用複雜度,多層裝飾的時候很容易導致不知道如何使用

Java程序員必備知識——常用設計模式:單列+裝飾器+策略+觀察者

三、策略模式

一個類的行為或其算法可以在運行時更改。這種類型的設計模式屬於行為型模式。在策略模式中,我們創建表示各種策略的對象和一個行為隨著策略對象改變而改變的 context 對象。策略對象改變 context 對象的執行算法。

比如利用策略模式優化過多 if else 代碼,將這些if else算法封裝成一個一個的類,任意地替換:

Java程序員必備知識——常用設計模式:單列+裝飾器+策略+觀察者

整體思路如下

  • 定義一個 InnerCommand 接口,其中有一個 process 函數交給具體的業務實現。
  • 根據自己的業務,會有多個類實現 InnerCommand 接口;這些實現類都會註冊到 Spring Bean 容器中供之後使用。
  • 通過客戶端輸入命令,從 Spring Bean 容器中獲取一個 InnerCommand 實例。
  • 執行最終的 process 函數。

缺點

  • 策略類會增多
  • 所有策略類都需要對外暴露。
Java程序員必備知識——常用設計模式:單列+裝飾器+策略+觀察者

四、觀察者模式

當對象間存在一對多關係時,則使用觀察者模式(Observer Pattern)。比如,當一個對象被修改時,則會自動通知它的依賴對象。定義對象間的一種一對多的依賴關係,當一個對象的狀態發生改變時,所有依賴於它的對象都得到通知並被自動更新。其實就是發佈訂閱模式,發佈者發佈信息,訂閱者獲取信息,訂閱了就能收到信息,沒訂閱就收不到信息

Java程序員必備知識——常用設計模式:單列+裝飾器+策略+觀察者

可以看到,該模式有四個角色:

  • 抽象被觀察者角色:也就是一個抽象主題,它把所有對觀察者對象的引用保存在一個集合中,每個主題都可以有任意數量的觀察者。抽象主題提供一個接口,可以增加和刪除觀察者角色。一般用一個抽象類和接口來實現。
  • 抽象觀察者角色:為所有的具體觀察者定義一個接口,在得到主題通知時更新自己。
  • 具體被觀察者角色:也就是一個具體的主題,在集體主題的內部狀態改變時,向所有登記過的觀察者發出通知。
  • 具體觀察者角色:實現抽象觀察者角色所需要的更新接口,以便使本身的狀態與製圖的狀態相協調。

優點

  • 松偶合、易維護。改變主題或觀察者中的一方,另一方不會受到影像。

缺點

  • 如果一個被觀察者對象有很多的直接和間接的觀察者的話,將所有的觀察者都通知到會花費很多時間。
  • 如果在觀察者和觀察目標之間有循環依賴的話,觀察目標會觸發它們之間進行循環調用,可能導致系統崩潰。
  • 觀察者模式沒有相應的機制讓觀察者知道所觀察的目標對象是怎麼發生變化的,而僅僅只是知道觀察目標發生了變化。

寫在最後

Java架構師丨蘇先生:專注於Java開發技術的研究與知識分享!

————END————

  • 轉發(好咖啡要和朋友一起品嚐,好文章也要和朋友一起分享!)
  • ...
  • 關注(持續更新)
  • ...

Java程序員必備知識——常用設計模式:單列+裝飾器+策略+觀察者


分享到:


相關文章: