手撕四種常用設計模式(工廠,策略,代理,單例)

工廠模式

一、工廠模式的總體好處

  1. 解耦:客戶端與具體實現類解耦,符合“開閉原則”。
  2. 統一創建:對象創建交由工廠處理,便于集中控制。
  3. 增強可維護性:新增對象種類時不需要大改動調用代碼。
  4. 便于擴展:易于管理產品族或產品等級結構。、

手寫靜態工廠模式,通過一個汽車靜態工廠負責創建汽車

特點:
  • 工廠類通過一個靜態方法來返回不同的對象。
  • 客戶端通過傳入參數決定創建哪個類。
? 優點:
  • 實現簡單,結構清晰。
  • 對客戶端隱藏了對象的具體創建過程。
?? 缺點:
  • 不符合開閉原則(新增產品需修改 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-elseswitch,結構更清晰。
  • 符合開閉原則:新增策略時無需改動已有代碼,只需增加新策略類。
  • 可復用性高:不同上下文可復用同一個策略類,提升代碼復用率
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(); 時,實際上分為三步:

  1. 為 uniqueInstance 分配內存
  2. 初始化 uniqueInstance
  3. 將 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;}
}

靜態內部類實現(線程安全)

  1. 延遲加載機制
    • 靜態內部類 SingletonHolder 不會在類加載時初始化,只有在首次調用 getUniqueInstance() 方法并訪問 SingletonHolder.INSTANCE 時才會被加載。
    • 此時 JVM 會保證 INSTANCE 的初始化過程是線程安全的,并且 僅執行一次。
  1. 線程安全保證
    • 由于類加載機制的特性,JVM 會通過 類初始化鎖(Class Initialization Lock) 確保 INSTANCE 的唯一性,無需額外同步代碼。
  1. 優勢總結
    • 懶加載:實例僅在需要時創建,節省資源。
    • 線程安全:依賴 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() {// 實際功能邏輯寫在這里}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/81496.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/81496.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/81496.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

阿里通義萬相 Wan2.1-VACE:開啟視頻創作新境界

2025 年 5 月 14 日,阿里巴巴為視頻創作領域帶來了重磅驚喜 —— 開源通義萬相 Wan2.1-VACE。這一模型堪稱視頻生成與編輯領域的集大成者,憑借其全面且強大的功能,為廣大創作者、開發者以及企業用戶開辟了全新的視頻創作天地。它打破了以往視…

自定義類、元組、字典和結構體對比——AutoCAD C# 開發中建立不同對象之間的聯系

以下是對它們的詳細分析和對比: 1. 自定義類(Class) 優勢 封裝性強:可以定義字段、屬性、方法和事件,實現復雜的行為和邏輯。繼承與多態:支持繼承體系,可通過接口或抽象類實現多態。引用類型…

MVC架構模式

mvc架構是一種常見的開發模式,以下是三個核心部分 Model(模型):負責應用程序的數據和業務邏輯。它與數據庫交互,處理數據的存儲、檢索和更新,是應用程序的核心業務所在。View(視圖)&#xff1a…

Python實例題:Python百行制作登陸系統

目錄 Python實例題 題目 python-login-systemPython 百行登錄系統腳本 代碼解釋 用戶數據庫: 注冊功能: 登錄功能: 主程序: 運行思路 注意事項 Python實例題 題目 Python百行制作登陸系統 python-login-systemPython…

uniapp使用全局組件,

在 Uniapp 中,如果你的組件是應用層組件(例如全局懸浮按鈕、全局通知欄等),并且希望它自動出現在所有頁面而無需在每個頁面模板中手動添加組件標簽,可以通過以下兩種方案實現: 方案一:通過 app.…

(8)python開發經驗

文章目錄 1 下載python2 pip安裝依賴無法訪問3 系統支持4 下載python文檔5 設置虛擬環境6 編譯安裝python 更多精彩內容👉內容導航 👈👉Qt開發 👈👉python開發 👈 1 下載python 下載地址盡量不要下載最新版…

【原創】基于視覺大模型gemma-3-4b實現短視頻自動識別內容并生成解說文案

📦 一、整體功能定位 這是一個用于從原始視頻自動生成短視頻解說內容的自動化工具,包含: 視頻抽幀(可基于畫面變化提取關鍵幀) 多模態圖像識別(每幀圖片理解) 文案生成(大模型生成…

每日算法刷題計劃Day5 5.13:leetcode數組3道題,用時1h

11. 26. 刪除有序數組中的重復項(簡單,雙指針) 26. 刪除有序數組中的重復項 - 力扣(LeetCode) 思想: 1.我的思想: 雙指針遍歷集合儲存已有元素 2.官方思想: 題目條件有序數組刪除重復元素,所以重復元素都是連續存在…

Transformer 架構在目標檢測中的應用:YOLO 系列模型解析

目錄 Transformer 架構在目標檢測中的應用:YOLO 系列模型解析 一、YOLO 模型概述 二、YOLO 模型的核心架構 (一)主干網絡 (二)頸部結構 (三)頭部結構 三、YOLO 模型的工作原理 &#xf…

一個完整的項目示例:taro開發微信小程序

前一周完成了一個項目,體測成績轉換的工具,沒做記錄,。這次計劃開發一個地圖應用小程序,記錄一下。方便給使用的人。 一、申請微信小程序,填寫相應的信息,取得開發者ID。這個要給騰訊地圖使用的。 二、申…

動態規劃-LCR 166.珠寶的最大價值-力扣(LeetCode)

一、題目解析 frame二維矩陣中每個值代表珠寶的價值,現在從左上角開始拿珠寶,只能向右或向下拿珠寶,到達右下角時停止拿珠寶,要求拿的珠寶價值最大。 二、算法解析 1.狀態表示 我們想要知道的是到達[i,j]為位置時的最大價值&am…

安裝nerdctl和buildkitd腳本命令

#!/bin/bash set -euo pipefail # 檢查是否以root權限運行 if [ "$(id -u)" -ne 0 ]; then echo "錯誤:請使用root權限或sudo運行本腳本" >&2 exit 1 fi # 檢測openEuler系統(兼容大小寫) detect_distrib…

實現視頻分片上傳 OSS

訪問 OSS 有兩種方式,本文用到的是使用臨時訪問憑證上傳到 OSS,不同語言版本的代碼參考: 使用STS臨時訪問憑證訪問OSS_對象存儲(OSS)-阿里云幫助中心 1.安裝并使用 首先我們要安裝 OSS: npm install ali-oss --save 接著我們…

動態規劃(3)學習方法論:構建思維模型

引言 動態規劃是算法領域中一個強大而優雅的解題方法,但對于許多學習者來說,它也是最難以掌握的算法范式之一。與貪心算法或分治法等直觀的算法相比,動態規劃往往需要更抽象的思維和更系統的學習方法。在前兩篇文章中,我們介紹了動態規劃的基礎概念、原理以及問題建模與狀…

elementplus el-tree 二次封裝支持配置刪除后展示展開或折疊編輯復選框懶加載功能

本文介紹了基于 ElementPlus 的 el-tree 組件進行二次封裝的 TreeView 組件,使用 Vue3 和 JavaScript 實現。TreeView 組件通過 props 接收樹形數據、配置項等,支持懶加載、節點展開/收起、節點點擊、刪除、編輯等操作。組件內部通過 ref 管理樹實例&…

2025年滲透測試面試題總結-安恒[實習]安全工程師(題目+回答)

網絡安全領域各種資源,學習文檔,以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各種好玩的項目及好用的工具,歡迎關注。 目錄 安恒[實習]安全工程師 一面 1. 自我介紹 2. 前兩段實習做了些什么 3. 中等難度的算法題 4. Java的C…

網絡編程中的直接內存與零拷貝

本篇文章會介紹 JDK 與 Linux 網絡編程中的直接內存與零拷貝的相關知識,最后還會介紹一下 Linux 系統與 JDK 對網絡通信的實現。 1、直接內存 所有的網絡通信和應用程序中(任何語言),每個 TCP Socket 的內核中都有一個發送緩沖區…

TransmittableThreadLocal使用場景

🚀 為什么要用 TransmittableThreadLocal?一文讀懂線程上下文傳遞問題 在 Java Web 開發中,我們經常用 ThreadLocal 來保存每個請求的用戶信息,例如 userId。但當我們使用線程池或異步方法(如 Async)時&am…

Milvus(24):全文搜索、文本匹配

1 全文搜索 全文搜索是一種在文本數據集中檢索包含特定術語或短語的文檔,然后根據相關性對結果進行排序的功能。該功能克服了語義搜索的局限性(語義搜索可能會忽略精確的術語),確保您獲得最準確且與上下文最相關的結果。此外&…

2000 元以下罕見的真三色光源投影儀:雷克賽恩Cyber Pro1重新定義入門級投影體驗

當性價比遇上技術瓶頸 在 2000元以下的1080P投影儀,單LCD 技術長期主導。而三色光源的DLP和3LCD真1080P都在4000元以上。 單LCD投影為純白光光源,依賴CF濾光膜導致光效低下,普遍存在" 色彩失真 " 等問題。數據顯示,該價…