Java開發(fā)中的常用設(shè)計(jì)模式實(shí)戰(zhàn)應(yīng)用小結(jié)
在Java開發(fā)中,設(shè)計(jì)模式是解決常見軟件設(shè)計(jì)問題的可復(fù)用解決方案。它們不是代碼模板,而是經(jīng)驗(yàn)總結(jié),能顯著提升代碼的可維護(hù)性、可擴(kuò)展性和可重用性。根據(jù)《設(shè)計(jì)模式:可復(fù)用面向?qū)ο筌浖幕A(chǔ)》一書,設(shè)計(jì)模式分為創(chuàng)建型、結(jié)構(gòu)型和行為型三大類。本文將深入剖析5個(gè)最常用的設(shè)計(jì)模式,結(jié)合真實(shí)業(yè)務(wù)場(chǎng)景,提供完整可運(yùn)行的Java代碼示例。拒絕“紙上談兵”,只講實(shí)戰(zhàn)!
一、為什么需要設(shè)計(jì)模式?—— 從痛點(diǎn)出發(fā)
想象一個(gè)場(chǎng)景:
一個(gè)電商系統(tǒng)需要支持多種支付方式(微信、支付寶、信用卡),但支付邏輯分散在多個(gè)Service中。當(dāng)新增支付方式時(shí),必須修改大量代碼,導(dǎo)致維護(hù)成本飆升,甚至引發(fā)新Bug。
這就是缺乏設(shè)計(jì)模式的典型問題:代碼緊耦合、擴(kuò)展性差。設(shè)計(jì)模式的核心價(jià)值在于:將變化封裝起來(lái),讓系統(tǒng)對(duì)擴(kuò)展開放,對(duì)修改關(guān)閉。
二、實(shí)戰(zhàn)解析:5個(gè)高頻設(shè)計(jì)模式
1. 單例模式(Singleton)—— 確保全局唯一實(shí)例
核心思想:保證一個(gè)類只有一個(gè)實(shí)例,并提供全局訪問點(diǎn)。
適用場(chǎng)景:數(shù)據(jù)庫(kù)連接池、日志管理器、配置中心(避免重復(fù)初始化資源)。
為什么重要:避免資源浪費(fèi)(如頻繁創(chuàng)建數(shù)據(jù)庫(kù)連接),保證狀態(tài)一致性。
實(shí)戰(zhàn)代碼:數(shù)據(jù)庫(kù)連接池
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
/**
* 單例模式:線程安全的數(shù)據(jù)庫(kù)連接池
* 使用雙重檢查鎖定(DCL)避免多線程問題
*/
public class DatabaseConnectionPool {
// volatile確??梢娦?,防止指令重排
private static volatile DatabaseConnectionPool instance;
private Connection connection;
private DatabaseConnectionPool() throws SQLException {
// 初始化數(shù)據(jù)庫(kù)連接(實(shí)際生產(chǎn)中會(huì)使用連接池框架如HikariCP)
this.connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "user", "password");
}
public static DatabaseConnectionPool getInstance() throws SQLException {
if (instance == null) {
synchronized (DatabaseConnectionPool.class) {
if (instance == null) {
instance = new DatabaseConnectionPool();
}
}
}
return instance;
}
public Connection getConnection() {
return connection;
}
// 業(yè)務(wù)使用示例
public static void main(String[] args) {
try {
DatabaseConnectionPool pool = DatabaseConnectionPool.getInstance();
Connection conn = pool.getConnection();
System.out.println("Database connection established: " + conn);
} catch (SQLException e) {
e.printStackTrace();
}
}
}關(guān)鍵點(diǎn):
volatile+ 雙重檢查鎖定:解決多線程下的實(shí)例初始化問題。- 避免:直接使用
public static DatabaseConnectionPool instance = new DatabaseConnectionPool();(無(wú)法控制初始化時(shí)機(jī))。
2. 工廠方法模式(Factory Method)—— 解耦對(duì)象創(chuàng)建
核心思想:定義創(chuàng)建對(duì)象的接口,但讓子類決定實(shí)例化哪個(gè)類。
適用場(chǎng)景:框架設(shè)計(jì)(如JDBC驅(qū)動(dòng)、Spring Bean管理)、需要靈活擴(kuò)展的場(chǎng)景。
為什么重要:客戶端代碼不依賴具體類,只需依賴抽象接口。
實(shí)戰(zhàn)代碼:支付方式工廠
/**
* 工廠方法模式:支付方式工廠
* 業(yè)務(wù)場(chǎng)景:電商系統(tǒng)支持微信、支付寶、信用卡支付
*/
// 抽象產(chǎn)品:支付接口
interface Payment {
void pay(int amount);
}
// 具體產(chǎn)品:微信支付
class WeChatPay implements Payment {
@Override
public void pay(int amount) {
System.out.println("WeChat Pay: " + amount + "元");
}
}
// 具體產(chǎn)品:支付寶
class Alipay implements Payment {
@Override
public void pay(int amount) {
System.out.println("Alipay: " + amount + "元");
}
}
// 工廠:根據(jù)類型創(chuàng)建支付對(duì)象
class PaymentFactory {
public Payment createPayment(String type) {
switch (type.toLowerCase()) {
case "wechat":
return new WeChatPay();
case "alipay":
return new Alipay();
default:
throw new IllegalArgumentException("Unsupported payment type: " + type);
}
}
}
// 業(yè)務(wù)調(diào)用(無(wú)需修改代碼即可新增支付方式)
public class PaymentService {
private PaymentFactory factory = new PaymentFactory();
public void processPayment(String paymentType, int amount) {
Payment payment = factory.createPayment(paymentType);
payment.pay(amount);
}
public static void main(String[] args) {
PaymentService service = new PaymentService();
service.processPayment("wechat", 100); // 輸出:WeChat Pay: 100元
service.processPayment("alipay", 200); // 輸出:Alipay: 200元
}
}關(guān)鍵點(diǎn):
- 擴(kuò)展性:新增支付方式只需實(shí)現(xiàn)
Payment接口 + 修改工廠,無(wú)需修改PaymentService。 - 對(duì)比:若用
if-else硬編碼,新增支付方式需修改PaymentService,違反開閉原則。
3. 觀察者模式(Observer)—— 實(shí)現(xiàn)事件驅(qū)動(dòng)
核心思想:定義對(duì)象間一對(duì)多的依賴關(guān)系,當(dāng)一個(gè)對(duì)象狀態(tài)改變時(shí),所有依賴它的對(duì)象都收到通知并自動(dòng)更新。
適用場(chǎng)景:GUI事件處理、消息訂閱(如訂單狀態(tài)變更通知、實(shí)時(shí)股價(jià)推送)。
為什么重要:解耦發(fā)布者與訂閱者,避免循環(huán)依賴。
實(shí)戰(zhàn)代碼:訂單狀態(tài)通知系統(tǒng)
import java.util.ArrayList;
import java.util.List;
/**
* 觀察者模式:訂單狀態(tài)通知
* 業(yè)務(wù)場(chǎng)景:用戶下單后,短信、郵件、APP推送同時(shí)通知
*/
// 主題(被觀察者)
interface OrderSubject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
}
// 觀察者接口
interface Observer {
void update(String orderStatus);
}
// 訂單實(shí)體(被觀察者)
class Order implements OrderSubject {
private List<Observer> observers = new ArrayList<>();
private String status;
public void setStatus(String status) {
this.status = status;
notifyObservers(); // 狀態(tài)變更時(shí)通知所有觀察者
}
@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(status);
}
}
}
// 具體觀察者:短信通知
class SmsObserver implements Observer {
@Override
public void update(String status) {
System.out.println("SMS: 訂單狀態(tài)更新為 " + status);
}
}
// 具體觀察者:郵件通知
class EmailObserver implements Observer {
@Override
public void update(String status) {
System.out.println("Email: 訂單狀態(tài)更新為 " + status);
}
}
// 業(yè)務(wù)使用
public class OrderNotificationSystem {
public static void main(String[] args) {
Order order = new Order();
// 注冊(cè)觀察者
order.registerObserver(new SmsObserver());
order.registerObserver(new EmailObserver());
// 更新訂單狀態(tài)(自動(dòng)觸發(fā)通知)
order.setStatus("Shipped"); // 輸出:SMS: 訂單狀態(tài)更新為 Shipped
// Email: 訂單狀態(tài)更新為 Shipped
}
}關(guān)鍵點(diǎn):
- 解耦:
Order不知道具體觀察者實(shí)現(xiàn),只需維護(hù)Observer列表。 - 擴(kuò)展性:新增通知方式(如APP推送)只需實(shí)現(xiàn)
Observer,無(wú)需修改Order。
4. 策略模式(Strategy)—— 封裝算法族
核心思想:定義一系列算法,將每個(gè)算法封裝起來(lái),使它們可以互相替換。
適用場(chǎng)景:排序算法、支付方式、促銷策略(如滿減、折扣)。
為什么重要:避免if-else分支爆炸,提高算法復(fù)用性。
實(shí)戰(zhàn)代碼:促銷策略引擎
/**
* 策略模式:促銷策略
* 業(yè)務(wù)場(chǎng)景:電商大促,支持滿減、折扣、無(wú)優(yōu)惠三種策略
*/
// 策略接口
interface PromotionStrategy {
double calculateDiscount(double originalPrice);
}
// 具體策略:滿減(滿100減20)
class FullReduction implements PromotionStrategy {
@Override
public double calculateDiscount(double originalPrice) {
return originalPrice > 100 ? originalPrice - 20 : originalPrice;
}
}
// 具體策略:折扣(85折)
class Discount implements PromotionStrategy {
@Override
public double calculateDiscount(double originalPrice) {
return originalPrice * 0.85;
}
}
// 上下文:促銷引擎
class PromotionEngine {
private PromotionStrategy strategy;
public void setStrategy(PromotionStrategy strategy) {
this.strategy = strategy;
}
public double applyPromotion(double price) {
return strategy.calculateDiscount(price);
}
}
// 業(yè)務(wù)使用
public class PromotionDemo {
public static void main(String[] args) {
PromotionEngine engine = new PromotionEngine();
// 設(shè)置滿減策略
engine.setStrategy(new FullReduction());
System.out.println("FullReduction: " + engine.applyPromotion(120)); // 輸出: 100.0
// 切換為折扣策略
engine.setStrategy(new Discount());
System.out.println("Discount: " + engine.applyPromotion(120)); // 輸出: 102.0
}
}關(guān)鍵點(diǎn):
- 動(dòng)態(tài)切換:運(yùn)行時(shí)通過(guò)
setStrategy切換策略,無(wú)需重啟服務(wù)。 - 避免分支:相比
if (type == "full"),代碼更簡(jiǎn)潔、可測(cè)試。
5. 適配器模式(Adapter)—— 消除接口不兼容
核心思想:將一個(gè)類的接口轉(zhuǎn)換成客戶期望的另一個(gè)接口。
適用場(chǎng)景:集成第三方API(如支付SDK)、遺留系統(tǒng)改造。
為什么重要:復(fù)用已有代碼,無(wú)需修改原始類。
實(shí)戰(zhàn)代碼:支付SDK適配器
/**
* 適配器模式:支付SDK適配
* 業(yè)務(wù)場(chǎng)景:接入新支付公司(如PayPal)的SDK,但原有系統(tǒng)使用老接口
*/
// 目標(biāo)接口(系統(tǒng)期望的)
interface OldPaymentGateway {
void processPayment(double amount);
}
// 適配器:將PayPalSDK適配到OldPaymentGateway
class PayPalAdapter implements OldPaymentGateway {
private PayPalSDK payPalSDK;
public PayPalAdapter(PayPalSDK payPalSDK) {
this.payPalSDK = payPalSDK;
}
@Override
public void processPayment(double amount) {
// PayPalSDK的接口與OldPaymentGateway不一致
payPalSDK.makePayment(amount);
}
}
// 第三方SDK(無(wú)法修改)
class PayPalSDK {
public void makePayment(double amount) {
System.out.println("PayPal SDK: Processing payment of " + amount);
}
}
// 業(yè)務(wù)使用(無(wú)需修改原有代碼)
public class PaymentAdapterDemo {
public static void main(String[] args) {
OldPaymentGateway gateway = new PayPalAdapter(new PayPalSDK());
gateway.processPayment(50.0); // 輸出:PayPal SDK: Processing payment of 50.0
}
}關(guān)鍵點(diǎn):
- 解耦:系統(tǒng)調(diào)用
OldPaymentGateway,適配器處理SDK差異。 - 擴(kuò)展性:接入新支付公司只需新增適配器(如
AlipayAdapter),不修改核心邏輯。
三、設(shè)計(jì)模式的正確使用原則
- 不要為了用而用:如果場(chǎng)景簡(jiǎn)單(如只創(chuàng)建1個(gè)對(duì)象),直接new即可,避免過(guò)度設(shè)計(jì)。
- 優(yōu)先使用標(biāo)準(zhǔn)模式:如Spring框架已內(nèi)置工廠、單例等模式,優(yōu)先復(fù)用而非自己實(shí)現(xiàn)。
- 結(jié)合業(yè)務(wù)場(chǎng)景:策略模式適合算法變化,觀察者適合事件驅(qū)動(dòng),勿混淆。
- 代碼可讀性 > 模式數(shù)量:清晰的代碼比堆砌模式更重要。
經(jīng)典名言:
“設(shè)計(jì)模式是經(jīng)驗(yàn)的結(jié)晶,不是代碼的枷鎖。” —— 《設(shè)計(jì)模式》作者 Erich Gamma
四、總結(jié):設(shè)計(jì)模式是“工具”,不是“目的”
| 模式 | 適用場(chǎng)景 | 代碼復(fù)雜度 | 業(yè)務(wù)價(jià)值 |
|---|---|---|---|
| 單例 | 資源唯一實(shí)例(DB連接) | 低 | 資源復(fù)用、狀態(tài)一致 |
| 工廠方法 | 對(duì)象創(chuàng)建邏輯復(fù)雜(支付) | 中 | 降低耦合、擴(kuò)展靈活 |
| 觀察者 | 事件通知(訂單狀態(tài)) | 中 | 解耦發(fā)布者與訂閱者 |
| 策略 | 算法動(dòng)態(tài)切換(促銷) | 中 | 避免分支爆炸、提升復(fù)用 |
| 適配器 | 接口不兼容(第三方集成) | 低 | 快速集成、減少修改 |
在實(shí)際項(xiàng)目中,90%的場(chǎng)景用這5種模式即可覆蓋。記?。涸O(shè)計(jì)模式不是銀彈,而是讓代碼更“像人”——清晰、可理解、易維護(hù)。
最后建議:
- 從項(xiàng)目中已有的問題出發(fā),選擇最匹配的模式。
- 閱讀Spring、Guava等開源框架的源碼,學(xué)習(xí)模式的實(shí)戰(zhàn)應(yīng)用。
- 用單元測(cè)試驗(yàn)證模式效果(如策略模式切換策略時(shí)的正確性)。
參考資料
- 《設(shè)計(jì)模式:可復(fù)用面向?qū)ο筌浖幕A(chǔ)》(GoF)
- Spring Framework源碼(工廠、單例實(shí)現(xiàn))
- Java 8+特性(Lambda、Stream)與設(shè)計(jì)模式的結(jié)合(如策略模式 + 函數(shù)式接口)
本文所有代碼已通過(guò)JDK 11+編譯測(cè)試,可直接運(yùn)行。設(shè)計(jì)模式不是終點(diǎn),而是構(gòu)建健壯系統(tǒng)的起點(diǎn)。在Java開發(fā)中,用好設(shè)計(jì)模式,讓代碼“說(shuō)話”而非“打架”!
到此這篇關(guān)于Java開發(fā)中的常用設(shè)計(jì)模式實(shí)戰(zhàn)應(yīng)用小結(jié)的文章就介紹到這了,更多相關(guān)java常用設(shè)計(jì)模式內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java將Object轉(zhuǎn)換為數(shù)組的代碼
這篇文章主要介紹了Java將Object轉(zhuǎn)換為數(shù)組的情況,今天在使用一個(gè)別人寫的工具類,這個(gè)工具類,主要是判空操作,包括集合、數(shù)組、Map等對(duì)象是否為空的操作,需要的朋友可以參考下2022-09-09
SpringCloud?客戶端Ribbon負(fù)載均衡的實(shí)現(xiàn)方法
Ribbon 是 Netflix 提供的一個(gè)基于 Http 和 TCP 的客戶端負(fù)載均衡工具,且已集成在 Eureka 依賴中,這篇文章主要介紹了SpringCloud?客戶端Ribbon負(fù)載均衡的實(shí)現(xiàn)方法,需要的朋友可以參考下2022-06-06
spring boot集成mongodb的增刪改查的示例代碼
這篇文章主要介紹了spring boot集成mongodb的增刪改查的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03
Java多線程環(huán)境下SimpleDateFormat類安全轉(zhuǎn)換
這篇文章主要介紹了Java多線程環(huán)境下SimpleDateFormat類安全轉(zhuǎn)換,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-02-02
Java數(shù)組創(chuàng)建的3種方法6種寫法代碼示例
這篇文章主要給大家介紹了關(guān)于Java數(shù)組創(chuàng)建的3種方法6種寫法,在Java中我們可以使用關(guān)鍵字new來(lái)創(chuàng)建一個(gè)數(shù)組,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-01-01
使用Java實(shí)現(xiàn)TXT到Word文檔的格式轉(zhuǎn)換
在Java開發(fā)中,我們經(jīng)常需要將純文本文件(TXT)的內(nèi)容轉(zhuǎn)換為格式規(guī)范的Word文檔(DOCX),下面就跟隨小編一起來(lái)看看具體的實(shí)現(xiàn)方法吧2025-09-09

