工廠模式
一、工廠模式的總體好處
- 解耦:客戶端與具體實現類解耦,符合“開閉原則”。
- 統一創建:對象創建交由工廠處理,便于集中控制。
- 增強可維護性:新增對象種類時不需要大改動調用代碼。
- 便于擴展:易于管理產品族或產品等級結構。、
手寫靜態工廠模式,通過一個汽車靜態工廠負責創建汽車
特點:
- 工廠類通過一個靜態方法來返回不同的對象。
- 客戶端通過傳入參數決定創建哪個類。
? 優點:
- 實現簡單,結構清晰。
- 對客戶端隱藏了對象的具體創建過程。
?? 缺點:
- 不符合開閉原則(新增產品需修改
createCar()
方法)。 - 工廠職責過重,產品一多代碼臃腫。
public class StaticFactoryModel {public static void main(String[] args){car car=new CarFactory.createCar("Tesla");
}
interface car{void drive();
}
class Tesla implements car{@Overridepublic void drive(){System.out.println("drive Tesla");}
}
class toyota implements car{@Overridepublic void drive(){System.out.println("drive toyota");}
}class CarFactory{public static car createCar(String type){switch(type){case"Tesla": return new Tesla();case"toyota":return new toyota();default:throw new IllegalArgumentException("UnKnow Car");}}
}
手寫工廠方法模式
特點:
- 將創建對象的工作延遲到子類,通過不同工廠子類創建不同對象。
? 優點:
- 滿足開閉原則,新增產品只需新增對應工廠。
- 結構清晰,職責單一,每個工廠只負責一種產品的創建。
?? 缺點:
- 類的數量變多,增加系統復雜度。
- 只能生產單一類型的產品。
package com;public class FactoryMethod {public static void main(String[] args) {phoneFactory factory=new iPhoneFactory();phone myphone=factory.createPhone();myphone.call();}
}interface phone{public void call();
}
class iPhone implements phone{@Overridepublic void call(){System.out.println("iPhone call");}
}
class Huawei implements phone{@Overridepublic void call(){System.out.println("Huawei call");}
}
interface phoneFactory{public phone createPhone();
}
class iPhoneFactory implements phoneFactory{@Overridepublic phone createPhone(){return new iPhone();}
}
class HuaweiFactory implements phoneFactory{@Overridepublic phone createPhone(){return new Huawei();}
}
手寫抽象工廠模式
? 特點:
- 一個工廠可以生產多個相關的產品(如電腦 + 操作系統)。
? 優點:
- 更強的擴展能力,可以生產“產品族”(多個相關產品)。
- 高度封裝了產品的創建細節,對客戶端透明。
?? 缺點:
- 不易新增“新產品”(比如新加一個 Printer 接口)需修改所有工廠。
- 抽象程度更高,理解成本稍大。
package com;public class AbstractFactory {public static void main(String[] args){ShowFactory factory=new WinFactory();Computee myCom=factory.createCom();Os myOs=factory.createOs();}
}interface Computee{public void use();
}
interface Os{public void call();
}class hp implements Computee{@Overridepublic void use() {System.out.println("useing window");}
}
class AppleCom implements Computee{@Overridepublic void use() {System.out.println("using apple");}
}class window implements Os{@Overridepublic void call() {System.out.println("calling window");}
}
class AppleOS implements Os{@Overridepublic void call() {System.out.println("calling apple");}
}
interface ShowFactory{Computee createCom();Os createOs();
}
class WinFactory implements ShowFactory{@Overridepublic Computee createCom() {return new hp();}@Overridepublic Os createOs() {return new window();}
}
//..另外一個工廠對應行為
策略模式
策略模式
上下文負責生成具體的策略類并且負責與客戶端交互
抽象策略類為抽象角色,通常由一個接口或者抽象類實現,給出所有的具體策略類需要的接口
具體策略類:是實現接口,提供具體算法或者行為
策略模式優點:
- 算法解耦:將行為或算法封裝在獨立策略類中,便于切換和擴展。
- 避免多重判斷:通過多態替代
if-else
或switch
,結構更清晰。 - 符合開閉原則:新增策略時無需改動已有代碼,只需增加新策略類。
- 可復用性高:不同上下文可復用同一個策略類,提升代碼復用率
package com;public class AbstractFactory {public static void main(String[] args){ShowFactory factory=new WinFactory();Computee myCom=factory.createCom();Os myOs=factory.createOs();}
}interface Computee{public void use();
}
interface Os{public void call();
}class hp implements Computee{@Overridepublic void use() {System.out.println("useing window");}
}
class AppleCom implements Computee{@Overridepublic void use() {System.out.println("using apple");}
}class window implements Os{@Overridepublic void call() {System.out.println("calling window");}
}
class AppleOS implements Os{@Overridepublic void call() {System.out.println("calling apple");}
}
interface ShowFactory{Computee createCom();Os createOs();
}
class WinFactory implements ShowFactory{@Overridepublic Computee createCom() {return new hp();}@Overridepublic Os createOs() {return new window();}
}
//..另外一個工廠對應行為
代理模式
代理模式(Proxy Pattern)是結構型設計模式的一種,
定義如下:
為其他對象提供一種代理以控制對這個對象的訪問。
這里使用jdk代理實現代理模式
優點:
- 增強功能:在不修改原始對象的情況下增加額外邏輯(如權限校驗、日志、事務等)。
- 解耦結構:將業務邏輯與通用功能分離,代碼更清晰、職責更單一。
- 靈活控制:可以在調用前后做一些處理,比如安全控制、延遲加載、訪問控制等。
- 支持動態擴展:通過 JDK 動態代理可根據接口生成代理對象,運行時更靈活。
package com;import java.awt.*;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;public class ProxyModel {
}interface UserDao{public void add();public void delete();
}
class UserDaoImpl implements UserDao{@Overridepublic void add() {System.out.println("adding");}@Overridepublic void delete() {System.out.println("deleteling");}
}
class UserProxy implements InvocationHandler{Object object;public UserProxy(Object obb){object=obb;}public Object getProxy(){return Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),this);}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("代理加強前");Object invoke = method.invoke(object, args);System.out.println("代理加強后");return invoke;}
}
單例模式
定義:
單例模式就是一個類只有一個實例,并且還提供這個實例的全局訪問點(避免一個全局使用的類頻繁創建和銷毀,耗費系統資源)
設計要素
- 一個私有的構造函數(確保只能由單例類自己創建實例)
- 一個私有的靜態變量(確保只有一個實例)
- 一個公有的靜態函數(給調用者提供調用方法)
單例類的構造方法不讓其他人修改和使用;并且單例類自己只創建一個實例,這個實例,其他人也無法修改和直接使用;然后單例類提供一個調用方法,想用這個實例,只能調用。這樣就確保了全局只創建了一次實例。
六種實現方式
懶漢式(線程不安全)
先不創建實例,當第一次被調用的時候再創建實例,延遲了實例化,不需要使用該類就不會實例化,節省了系統資源
線程不安全,如果多個線程同時進入了lazyd==null,此時如果還沒有實例化,多個線程就會進行實例化,導致實例化了多個實例
package com;public class LazyD {private static LazyD lazyd;private LazyD(){}public static LazyD getUniqueInstance(){if(lazyd==null){lazyd=new LazyD();}return lazyd;}
}
餓漢式不管使用還是不使用這個實例,直接實例化好實例即可,然后如果需要使用的時候,直接調用方法即可
優點:提前實例化了,避免了線程不安全的問題
缺點:直接實例花了這個實例,不會再延遲實例化,如果系統沒有使用這個實例,就會導致操作系統的資源浪費
package com;public class HungryD {private static HungryD uniqueInstance=new HungryD();private HungryD(){};public static HungryD getUniqueInstance(){return uniqueInstance;}
}
懶漢式(線程安全)
和基本的懶漢式的區別就是在get方法上 加了一把 鎖。如此一來,多個線程訪問,每次只有拿到鎖的的線程能夠進入該方法,避免了多線程不安全問題的出現。
package com;public class Singletion {private static Singletion uniqueInstance;private Singletion(){};public static synchronized Singletion getUniqueInstance(){if(uniqueInstance==null){uniqueInstance=new Singletion();}return uniqueInstance;}
}
雙重校驗鎖實現(線程安全)
雙重檢查鎖定(Double-Check Locking)是一種對線程安全的懶漢式單例模式的優化。傳統的線程安全懶漢式存在性能問題——即使單例已經創建,每次調用仍然需要獲取鎖,導致性能下降。
而雙重檢查鎖定通過在加鎖前先判斷實例是否已存在,避免了不必要的鎖開銷:
- 如果實例已創建,直接返回實例,不進入加鎖代碼塊,提升了效率。
- 如果實例未創建,多個線程同時進入時,由于加鎖機制,只有一個線程能夠進入鎖內創建實例,保證線程安全。
因此,只有在首次實例化時會發生線程阻塞,之后的調用都不會再產生鎖競爭,從而實現了高效且安全的延遲初始化。
核心就是對比懶漢式的線程安全版本有性能提升
還有就是使用volatile關鍵字修飾uniqueInstance實例變量的原因如下
執行 uniqueInstance = new Singleton();
時,實際上分為三步:
- 為 uniqueInstance 分配內存
- 初始化 uniqueInstance
- 將 uniqueInstance 指向分配的內存地址
雖然正常順序是 1 → 2 → 3,但 JVM 可能因指令重排導致執行順序變為 1 → 3 → 2。
在單線程環境中這不會有問題,但在多線程環境下可能導致安全隱患:例如線程 A 執行了步驟 1 和 3,還未完成初始化(步驟 2),線程 B 看到 uniqueInstance 非空后直接使用它,結果是使用了未初始化的實例。
為避免這種情況,使用 volatile
關鍵字修飾 uniqueInstance,可以禁止指令重排,確保多線程環境下實例的正確初始化和可見性,保證線程安全。
package com;public class Singletion {private volatile static Singletion uniqueInstance;private Singletion(){};public static Singletion getUniqueInstance(){if(uniqueInstance==null){synchronized (Singletion.class){if(uniqueInstance==null){uniqueInstance=new Singletion();}}}return uniqueInstance;}
}
靜態內部類實現(線程安全)
- 延遲加載機制
-
- 靜態內部類
SingletonHolder
不會在類加載時初始化,只有在首次調用getUniqueInstance()
方法并訪問SingletonHolder.INSTANCE
時才會被加載。 - 此時 JVM 會保證
INSTANCE
的初始化過程是線程安全的,并且 僅執行一次。
- 靜態內部類
- 線程安全保證
-
- 由于類加載機制的特性,JVM 會通過 類初始化鎖(Class Initialization Lock) 確保
INSTANCE
的唯一性,無需額外同步代碼。
- 由于類加載機制的特性,JVM 會通過 類初始化鎖(Class Initialization Lock) 確保
- 優勢總結
-
- 懶加載:實例僅在需要時創建,節省資源。
- 線程安全:依賴 JVM 的類加載機制,無需雙重檢查鎖(DCL)或
synchronized
。 - 高性能:無鎖競爭,訪問效率高
package com;public class Singletion {private Singletion(){};private static class SingletionHolder{private static final Singletion INSTANCE=new Singletion()l}public static Singletion getUniqueInstance(){return SingletionHolder.INSTANCE;}
}
枚舉類實現(線程安全)
枚舉類的創建就是線程安全的,任何情況下都是單例的
枚舉實現單例時,其實例的創建由 JVM 保證線程安全,且天然是單例。
優點
- 寫法簡潔
- 天然線程安全
- 自動防止反射和反序列化攻擊
關于反序列化問題
- 序列化:將 Java 對象轉換為字節序列
- 反序列化:根據字節序列重建 Java 對象
常規單例模式在反序列化時可能會創建新的實例,破壞單例性。
為了避免這一問題,通常需要重寫 readResolve()
方法來確保反序列化返回同一個實例:
package com;public enum Singletion {INSTANCE;// 添加業務邏輯方法public void using() {// 實際功能邏輯寫在這里}
}