設計模式是前人總結的解決特定問題的最佳實踐。23 種經典設計模式分為三大類:創建型、結構型、行為型。
設計模式是「術」,設計原則是「道」。理解原則比背誦模式更重要。
創建型模式#
解決物件創建的問題,將物件的創建與使用分離。
單例模式 (Singleton)#
意圖:確保一個類別只有一個實體,並提供全局訪問點。
適用場景:
- 組態類別
- 連線池
- 日誌記錄器
實作方式:
// 1. 餓漢式(推薦)
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {}
public static Singleton getInstance() { return INSTANCE; }
}
// 2. 雙重檢查鎖定
public class Singleton {
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
// 3. 靜態內部類別(延遲載入 + 執行緒安全)
public class Singleton {
private Singleton() {}
private static class Holder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() { return Holder.INSTANCE; }
}單例模式的缺點:
- 隱藏依賴關係
- 難以測試
- 擴展困難
工廠模式 (Factory)#
三種工廠:
| 類型 | 說明 | 使用時機 |
|---|---|---|
| 簡單工廠 | 一個工廠類別 | 物件類型少 |
| 工廠方法 | 每個產品一個工廠 | 需要擴展 |
| 抽象工廠 | 創建產品族 | 多系列產品 |
簡單工廠:
public class ParserFactory {
public static Parser createParser(String type) {
switch (type) {
case "json": return new JsonParser();
case "xml": return new XmlParser();
default: throw new IllegalArgumentException();
}
}
}工廠方法:
public interface ParserFactory {
Parser createParser();
}
public class JsonParserFactory implements ParserFactory {
@Override
public Parser createParser() { return new JsonParser(); }
}何時使用工廠模式?
- 創建邏輯複雜
- 物件類型由執行時決定
- 需要將創建與使用解耦
建造者模式 (Builder)#
意圖:分離複雜物件的構建與表示。
適用場景:
- 參數多且可選
- 需要校驗參數
- 創建不可變物件
public class ResourcePoolConfig {
private final String name;
private final int maxTotal;
private final int maxIdle;
private final int minIdle;
private ResourcePoolConfig(Builder builder) {
this.name = builder.name;
this.maxTotal = builder.maxTotal;
this.maxIdle = builder.maxIdle;
this.minIdle = builder.minIdle;
}
public static class Builder {
private String name;
private int maxTotal = 8;
private int maxIdle = 8;
private int minIdle = 0;
public Builder name(String name) {
this.name = name;
return this;
}
public Builder maxTotal(int maxTotal) {
if (maxTotal <= 0) throw new IllegalArgumentException();
this.maxTotal = maxTotal;
return this;
}
// 其他 setter...
public ResourcePoolConfig build() {
if (name == null || name.isEmpty()) {
throw new IllegalArgumentException("name is required");
}
return new ResourcePoolConfig(this);
}
}
}
// 使用
ResourcePoolConfig config = new ResourcePoolConfig.Builder()
.name("dbPool")
.maxTotal(16)
.build();原型模式 (Prototype)#
意圖:通過複製現有物件來創建新物件。
適用場景:創建成本高(如需要讀取資料庫或網路請求)
public class SearchWord implements Cloneable {
private String keyword;
private long lastUpdateTime;
@Override
public SearchWord clone() {
try {
return (SearchWord) super.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
}注意深拷貝與淺拷貝的區別!
結構型模式#
處理類別或物件的組合,形成更大的結構。
代理模式 (Proxy)#
意圖:為另一個物件提供代理以控制對它的訪問。
應用:
- 遠端代理(RPC)
- 虛擬代理(延遲載入)
- 保護代理(權限控制)
- 日誌、監控
public interface UserService {
void register(String username);
}
// 代理類別
public class UserServiceProxy implements UserService {
private UserService target;
public UserServiceProxy(UserService target) {
this.target = target;
}
@Override
public void register(String username) {
long start = System.currentTimeMillis();
target.register(username);
long end = System.currentTimeMillis();
System.out.println("Time: " + (end - start) + "ms");
}
}動態代理:
// JDK 動態代理
UserService proxy = (UserService) Proxy.newProxyInstance(
UserService.class.getClassLoader(),
new Class<?>[] { UserService.class },
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) {
// 前置處理
Object result = method.invoke(target, args);
// 後置處理
return result;
}
}
);裝飾者模式 (Decorator)#
意圖:動態地為物件添加職責。
與繼承的區別:裝飾者可以在執行時組合功能。
flowchart LR
subgraph 裝飾器層層包裹
A[CompressionDecorator] --> B[EncryptionDecorator]
B --> C[FileDataSource]
end
D[寫入資料] --> A
C --> E[檔案]// Java I/O 是裝飾者模式的經典應用
InputStream in = new BufferedInputStream(
new FileInputStream("file.txt"));public interface DataSource {
void writeData(String data);
String readData();
}
// 裝飾者基類別
public abstract class DataSourceDecorator implements DataSource {
protected DataSource wrappee;
public DataSourceDecorator(DataSource source) {
this.wrappee = source;
}
}
// 具體裝飾者
public class EncryptionDecorator extends DataSourceDecorator {
@Override
public void writeData(String data) {
wrappee.writeData(encrypt(data));
}
}
// 使用:可以疊加多個裝飾
DataSource source = new CompressionDecorator(
new EncryptionDecorator(
new FileDataSource("file.txt")));適配器模式 (Adapter)#
意圖:將一個介面轉換成另一個介面。
// 舊介面
public interface OldLogger {
void log(String msg);
}
// 新介面
public interface NewLogger {
void debug(String msg);
void info(String msg);
}
// 適配器
public class LoggerAdapter implements NewLogger {
private OldLogger oldLogger;
@Override
public void debug(String msg) {
oldLogger.log("[DEBUG] " + msg);
}
@Override
public void info(String msg) {
oldLogger.log("[INFO] " + msg);
}
}其他結構型模式#
| 模式 | 意圖 | 典型應用 |
|---|---|---|
| 橋接 (Bridge) | 分離抽象與實作 | JDBC |
| 門面 (Facade) | 提供統一的高層介面 | SLF4J |
| 組合 (Composite) | 統一處理單個與組合物件 | 檔案系統 |
| 享元 (Flyweight) | 共享細粒度物件 | Integer 快取 |
行為型模式#
處理物件之間的演演算法和職責分配。
觀察者模式 (Observer)#
意圖:定義一對多的依賴關係,當一個物件改變狀態時,通知所有依賴者。
public interface Observer {
void update(String message);
}
public class Subject {
private List<Observer> observers = new ArrayList<>();
public void attach(Observer o) { observers.add(o); }
public void detach(Observer o) { observers.remove(o); }
public void notifyObservers(String message) {
for (Observer o : observers) {
o.update(message);
}
}
}應用:
- 事件監聽
- 訊息發布訂閱
- 框架回呼
策略模式 (Strategy)#
意圖:定義一系列演演算法,讓它們可以互換。
public interface DiscountStrategy {
double calculateDiscount(double price);
}
public class NormalDiscount implements DiscountStrategy {
@Override
public double calculateDiscount(double price) {
return price;
}
}
public class VipDiscount implements DiscountStrategy {
@Override
public double calculateDiscount(double price) {
return price * 0.8;
}
}
// 使用
public class Order {
private DiscountStrategy strategy;
public void setStrategy(DiscountStrategy strategy) {
this.strategy = strategy;
}
public double getFinalPrice(double price) {
return strategy.calculateDiscount(price);
}
}模板方法模式 (Template Method)#
意圖:定義演演算法骨架,允許子類別重新定義某些步驟。
public abstract class AbstractClass {
// 模板方法
public final void templateMethod() {
step1();
step2();
step3();
}
protected abstract void step1();
protected abstract void step2();
protected void step3() {
// 預設實作,子類別可覆寫
}
}應用:Servlet、JUnit、Spring
責任鏈模式 (Chain of Responsibility)#
意圖:將請求沿著處理者鏈傳遞。
flowchart LR
A[請求] --> B[Handler 1<br>認證]
B -->|通過| C[Handler 2<br>授權]
C -->|通過| D[Handler 3<br>日誌]
D -->|通過| E[實際處理]
B -->|失敗| F[拒絕]
C -->|失敗| Fpublic abstract class Handler {
protected Handler next;
public void setNext(Handler next) {
this.next = next;
}
public abstract void handle(Request request);
}
public class AuthHandler extends Handler {
@Override
public void handle(Request request) {
if (!authenticate(request)) {
throw new AuthException();
}
if (next != null) {
next.handle(request);
}
}
}應用:Servlet Filter、Spring Interceptor
其他行為型模式#
| 模式 | 意圖 | 典型應用 |
|---|---|---|
| 狀態 (State) | 根據狀態改變行為 | 狀態機 |
| 迭代器 (Iterator) | 順序訪問集合元素 | Java Collection |
| 訪問者 (Visitor) | 分離資料結構與操作 | 編譯器 AST |
| 備忘錄 (Memento) | 保存和恢復狀態 | 撤銷功能 |
| 命令 (Command) | 將請求封裝成物件 | 事務、巨集 |
| 中介者 (Mediator) | 封裝物件交互 | 聊天室 |
| 解釋器 (Interpreter) | 定義語言語法 | 正則表達式 |
設計模式總覽#
mindmap
root((23 種設計模式))
創建型 5 種
單例 Singleton
工廠方法 Factory Method
抽象工廠 Abstract Factory
建造者 Builder
原型 Prototype
結構型 7 種
代理 Proxy
裝飾者 Decorator
適配器 Adapter
橋接 Bridge
門面 Facade
組合 Composite
享元 Flyweight
行為型 11 種
觀察者 Observer
策略 Strategy
模板方法 Template Method
責任鏈 Chain of Responsibility
狀態 State
迭代器 Iterator
訪問者 Visitor
備忘錄 Memento
命令 Command
中介者 Mediator
解釋器 Interpreter如何選擇設計模式
創建物件
- 只需一個實體 → 單例
- 創建邏輯複雜 → 工廠
- 參數多且可選 → 建造者
- 創建成本高 → 原型
組合物件
- 控制訪問 → 代理
- 動態添加功能 → 裝飾者
- 介面轉換 → 適配器
- 簡化子系統 → 門面
物件交互
- 一對多通知 → 觀察者
- 演演算法可替換 → 策略
- 固定流程可變步驟 → 模板方法
- 多個處理者 → 責任鏈
- 狀態決定行為 → 狀態
避免過度設計:不要為了使用設計模式而使用。當問題出現時再重構引入。