Java設(shè)計模式之創(chuàng)建者模式詳解
前言
創(chuàng)建者模式,顧名思義,就是提供友好的創(chuàng)建對象的方式 ,對象都是 new 出來的,但是在一些情況下,這種方式不是很友好
首先,它不夠直觀,其次,在一些情況下,這樣創(chuàng)建的對象不滿足要求。
比如 : 當(dāng)我們需要創(chuàng)建單例的對象,那我們就不能每次用的時候就重新 new, 這樣很明顯是不合理的 。
一、工廠模式
引入工廠類的原因 : 是因為我們需要兩個或者兩個以上的工廠生產(chǎn)對象,假如我們就需要一個對象的工廠,那就沒必要引入工廠類了 。
1. 簡單工廠模式
就一個工廠,它具有不同的方法,生產(chǎn)出不同的產(chǎn)品 。
2. 工廠模式
我們需要兩個及以上的工廠,所以抽象出一個工廠接口,其他工廠實現(xiàn)類實現(xiàn)這個抽象接口,重寫創(chuàng)建方法 。
3. 抽象工廠模式
涉及到產(chǎn)品族問題 , 調(diào)用的時候先選擇工廠,再調(diào)用其方法,生成相應(yīng)的產(chǎn)品。
工廠模式在 Spring 框架中的使用
Spring 框架通過反射機制實現(xiàn)工廠模式,從而降低了程序的耦合程度 。
具體實現(xiàn)思路為 : 讀取配置文件中相關(guān)值, 然后通過反射 Class.forName 得到該值對應(yīng)的類,再通過 newInstance() 獲取該類的實例返回 。
我們調(diào)用時只需要 : Factory.getXXX("我們定義在配置文件中的 Bean 名稱")
二、單例模式
1. 餓漢式實現(xiàn)
私有構(gòu)造方法 + 創(chuàng)建靜態(tài)實例
public class Singleton { // 私有構(gòu)造方法 private Singleton() {}; // 創(chuàng)建私有靜態(tài)實例 private static Singleton instance = new Singleton(); public static Singleton getInstance() { return instance; } }
缺點 : 不想用 Singleton 實例時,它也生成了,因為是 static 的, 這個類被第一次使用時就會被生成 。
2. 飽漢式實現(xiàn) 雙重檢查機制原理
私有構(gòu)造方法 + volatile 修飾 + 雙重判空,加鎖
public class Singleton { // 私有構(gòu)造方法 private Singleton() {} // valitale 修飾 private static volatile Singleton instance = null; public static Singleton getInstance() { // 雙重檢查是否為 null if (instance == null) { // 加鎖 synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } }
為什么要在實例化時加 synchronized 鎖, 不是已經(jīng)用 volatile 關(guān)鍵字修飾了嗎 ?
因為 volatile 只可以保證變量的可見性 和 防止實例化時指令重排 ,它不能保證操作的原子性 。我們要求只能有一個線程去實例化,不能一堆線程一起進去實例化 。
不是已經(jīng)有 synchronized 鎖了嗎,為什么還要 volatile 關(guān)鍵字修飾 ?
因為我們得保證 一個線程實例化 Singleton 后,其他線程可見,直接返回就行,所以我們需要 volatile 去保證 Singleton 在多個線程中的可見性 , 不被 JVM 緩存。
除此之外,它也通過防止對象創(chuàng)建時的指令重排,而使得線程安全 。
什么是指令重排 ? 創(chuàng)建對象時,先分配內(nèi)存空間,然后實例指向該空間地址,然后才初始化 。
正常情況應(yīng)該是, 分配內(nèi)存空間 — 初始化 — 指向該空間地址
如果不用 volatile 修飾, 發(fā)生指令重排時, 創(chuàng)建對象的線程只是指向了空間地址,但是還沒初始化,但是因為已經(jīng)指向地址了
下一個線程在第一重 null 檢查時,判斷為非 null,直接返回了,但是返回的實例還沒初始化 。這是線程不安全的,所以需要 volatile 。
3. 嵌套類實現(xiàn)
利用嵌套類可以 訪問外部類 的屬性和方法 。在嵌套類中實例化 Singleton 。
public class Singleton { private Singleton() {} // 利用嵌套類可以訪問外部類的屬性和方法 private static class Holder { private static Singleton instance = new Singleton(); } public static Singleton getInstance() { return Holder.instance; } }
4. 枚舉實現(xiàn) *
public enum Singleton { INSTANCE; }
據(jù)說被谷歌大佬作為實現(xiàn)單例的最佳實踐 —— 即簡單又安全
下面我們來一起簡單看一下枚舉為什么可以實現(xiàn)單例 ?
我們自定義的枚舉類 默認 繼承 Enum, Enum 的源碼如下 :
public abstract class Enum<E extends Enum<E>> implements Comparable<E>, Serializable { // 枚舉項的名字 , 程序員用 toString 方法而不是這個 private final String name; public final String name() { return name; } // 程序員用不到的,給使用枚舉類型的 API 使用,比如 java.util.EnumSet private final int ordinal; public final int ordinal() { return ordinal; } // 程序員用不到的,給編譯器發(fā)出的代碼,告訴編譯器實例化這個枚舉類 protected Enum(String name, int ordinal) { this.name = name; this.ordinal = ordinal; } public String toString() { return name; } public final boolean equals(Object other) { return this==other; } public final int hashCode() { return super.hashCode(); } // 克隆枚舉類拋出異常,這保證了枚舉永遠不會被克隆,這對于保持它們的 "單例 "狀態(tài)是必要的。 protected final Object clone() throws CloneNotSupportedException { throw new CloneNotSupportedException(); } public final int compareTo(E o) { Enum<?> other = (Enum<?>)o; Enum<E> self = this; if (self.getClass() != other.getClass() && // optimization self.getDeclaringClass() != other.getDeclaringClass()) throw new ClassCastException(); return self.ordinal - other.ordinal; } @SuppressWarnings("unchecked") public final Class<E> getDeclaringClass() { Class<?> clazz = getClass(); Class<?> zuper = clazz.getSuperclass(); return (zuper == Enum.class) ? (Class<E>)clazz : (Class<E>)zuper; } // 返回這個枚舉常量的枚舉類型所對應(yīng)的Class對象 public static <T extends Enum<T>> T valueOf(Class<T> enumType, String name) { T result = enumType.enumConstantDirectory().get(name); if (result != null) return result; if (name == null) throw new NullPointerException("Name is null"); throw new IllegalArgumentException( "No enum constant " + enumType.getCanonicalName() + "." + name); } protected final void finalize() { } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { throw new InvalidObjectException("can't deserialize enum"); } private void readObjectNoData() throws ObjectStreamException { throw new InvalidObjectException("can't deserialize enum"); } }
我們定義的枚舉類里面的每個枚舉項都是一個 Enum, 這個 Enum 都是 static final 類型的,也就是說不容許被修改,初次之外我們的自定義的枚舉類默認私有構(gòu)造方法(體現(xiàn)在編譯完之后),不能被程序員改成 public 的
我們再翻譯一下,翻譯成我覺得更好理解的
首先, 枚舉類里的枚舉項都是 枚舉類 Enum 的實例,枚舉幫我們自動處理了一些東西,簡化了代碼其幫我們進行的操作就是對每個枚舉項私有了構(gòu)造方法,然后實例定義成 static final 類型的,就是相當(dāng)于我們餓漢式的實現(xiàn)代碼,其幫我們封裝好了,就是其幫我們封裝好了餓漢式的實現(xiàn)代碼除此之外,我們枚舉類的實例只能有我們在枚舉類里的那幾個,對單例來說只有那一個,不容許再進行擴張實例 ,只能有那一個。
三、建造者模式
鏈式調(diào)用方法,實現(xiàn)對象的創(chuàng)建 。為什么優(yōu)于 new ?
首先,一個設(shè)計模式是最佳實踐是相對其使用場景說的。
建造者模式適用于要創(chuàng)建的對象非常復(fù)雜的情況,構(gòu)造者模式讓這個復(fù)雜類的裝配變得按部就班又直觀。
舉個栗子
MyBatis 中讀取配置文件構(gòu)建配置類,這個配置類超級大 。所以使用了建造者模式,把這個復(fù)雜類的構(gòu)建拆分成了好多 builder,根據(jù)傳入的參數(shù)的不同進行創(chuàng)建 。
四、原型模式
對于原型模式,首先我們需要記住的就是實現(xiàn) Cloneable 接口,并且重寫 clone 方法 。(注意深淺克隆的區(qū)別)
其實,按照我對原型模式的理解,就是新建了一個模板。然后,我們不斷去克隆這個模版,更改模版中的一些值。
到此這篇關(guān)于Java設(shè)計模式之創(chuàng)建者模式詳解的文章就介紹到這了,更多相關(guān)Java創(chuàng)建者模式內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java編程中的防轉(zhuǎn)義和轉(zhuǎn)義技巧匯總
在編程過程中,我們常常需要處理特殊字符和特定上下文,以確保生成的內(nèi)容在正確的環(huán)境中能夠被解析和顯示,本文將介紹一些常見的防轉(zhuǎn)義或者轉(zhuǎn)義處理的編程技巧,需要的可以參考一下2023-07-07SpringBoot使用Nacos動態(tài)配置數(shù)據(jù)源的方法
這篇文章主要介紹了SpringBoot使用Nacos動態(tài)配置數(shù)據(jù)源的方法,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-03-03springboot使用校驗框架validation校驗的示例
這篇文章主要介紹了springboot使用校驗框架validation校驗的示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-02-02Java?MyBatis實戰(zhàn)之QueryWrapper中and和or拼接技巧大全
在Java中QueryWrapper是MyBatis-Plus框架中的一個查詢構(gòu)造器,它提供了豐富的查詢方法,其中包括and和or方法,可以用于構(gòu)建復(fù)雜的查詢條件,這篇文章主要給大家介紹了關(guān)于Java?MyBatis實戰(zhàn)之QueryWrapper中and和or拼接技巧的相關(guān)資料,需要的朋友可以參考下2024-07-07Spring?Data?JPA實現(xiàn)審計功能過程詳解
Spring?Data?JPA為跟蹤持久性層的變化提供了很好的支持。通過使用審核,我們可以存儲或記錄有關(guān)實體更改的信息,例如誰創(chuàng)建或更改了實體以及何時進行更改2023-02-02Java中Validated、Valid 、Validator區(qū)別詳解
本文主要介紹了Java中Validated、Valid 、Validator區(qū)別,有時候面試的時候會被問到,他們的區(qū)別你知道幾個,本文就來詳細的介紹一下2021-08-08