談談Java常用類庫中的設計模式 - Part Ⅰ
### 工廠方法 (Factory Method) >定義:定義一個建立物件的介面,讓子類決定例項化哪一個類。工廠方法使一個類的例項化延遲到其子類。 場景:明確計劃不同條件下建立不同例項時。 型別:建立型 若要找工廠方法在JAVA原生類庫中最貼切的對照物,非 **Supplier
There is no requirement that a new or distinct result be returned each * time the supplier is invoked. * *
This is a functional interface
* whose functional method is {@link #get()}.
*
* @param
### 享元 (Flyweight)
>定義:運用共享技術有效地支援大量細粒度的物件。
場景:應用使用大量物件,造成龐大的儲存開銷;物件中的大多數狀態可以移至外部,剩下的部分可以共享。
型別:結構型
JDK類庫中使用了大量的***靜態工廠***(泛指建立物件的靜態類/靜態方法),這些靜態工廠有一個重要的作用:為重複的呼叫返回相同的物件。使類成為例項受控的類(instance-controlled),這實際上就是享元的思想。
舉個例子,下面是 **Boolean.valueOf(boolean b)** 的程式碼片段。
~~~
/**
* The {@code Boolean} object corresponding to the primitive
* value {@code true}.
*/
public static final Boolean TRUE = new Boolean(true);
/**
* The {@code Boolean} object corresponding to the primitive
* value {@code false}.
*/
public static final Boolean FALSE = new Boolean(false);
public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}
~~~
Boolean 作為布林型別的包裝類,進行了例項控制。 因為布林型別的值只有 True 和 False ,除此之外沒有其他狀態欄位,所以類庫設計者選擇在類載入時初始化兩個不可變例項,在靜態工廠中不建立物件。
**這也表明 Boolean.valueOf 返回的例項在執行==和equals時結果一致**。
其他包裝型別如Byte,Short,Integer也有類似的設計:使用名為**XCache**(X表示型別名)的私有內部類中儲存值在 -128 ~ 127 之間共256個例項,並在靜態工廠中使用。**在面試中經常碰見的數值包裝類“==”問題,考點就在這裡。**
### 橋接(Bridge)
>定義:將抽象部分與他的實現部分分離,使它們都可以獨立地變化。
場景:實現系統可能有多個角度分類,每一種角度都可能變化;在構件的抽象化和具體化之間增加更多的靈活性,避免兩個層次之間的靜態繼承關係;控制系統中繼承層次過多過深。
型別:結構型
首先了解一個概念:**服務提供者框架(Service Provider Framework)**(下文簡稱SPF)。
> 服務提供者框架指這樣一個系統:多個服務提供者實現一個服務,系統為服務提供者的客戶端提供多個實現,並把它們從多個實現中解耦出來。
>它由4種元件組成:
>
>服務介面:需被實現的介面或抽象類
>提供者註冊API:實現類用來註冊自己到SPF中的
>服務訪問API:客戶端用來獲取實現的
>服務提供者介面:實現類的工廠物件,用來建立實現類的例項,是可選的
SPF模式的應用是如此廣泛,其實現的變體也有很多。如Java 6提供的標準實現ServiceLoader,還有Spring、Guice這樣的依賴注入框架。但我選擇舉一個大家更為熟悉的例子:**JDBC**。
使用JDBC與資料庫互動是每個Java程式設計師的必經之路,而它的設計實際上也是SPF模式:
服務介面 -> **Connection**
提供者註冊API:**DriverManager.registerDriver**
服務訪問API:**DriverManager.getConnection**
服務提供者介面:**Driver**
**想要使用不同的資料庫連線實現,只需通過服務訪問API切換即可。這體現了橋接中將抽象與實現分離的精神。**
*(實際上如果載入多個數據庫驅動,DriverManager會逐個嘗試連線,並返回連線成功的例項。並不能人為選擇提供者,但可以通過更改提供者註冊程式碼來實現。)*
**Druid、Hikari等現代連線池的實現往往比JDBC定義的服務介面更加豐富,如監控、外掛鏈、SQL日誌等等,這體現了橋接當中的獨立變化。**
---
#### 參考:
[1] Play With Java ServiceLoader And Forget About Dependency Injection Frameworks - (2016/10/02)
https://dzone.com/articles/play-with-java-serviceloader-forget-about-dependen
[2] *Effective Java* - 機械工業出版社 - Joshua Bloch (2017/11)
[3] 《大話設計模式》 - 清華大學出版社 - 陳杰 (200