設計模式是前人總結的解決特定問題的最佳實踐。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 -->|失敗| F
public 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
如何選擇設計模式

創建物件

  • 只需一個實體 → 單例
  • 創建邏輯複雜 → 工廠
  • 參數多且可選 → 建造者
  • 創建成本高 → 原型

組合物件

  • 控制訪問 → 代理
  • 動態添加功能 → 裝飾者
  • 介面轉換 → 適配器
  • 簡化子系統 → 門面

物件交互

  • 一對多通知 → 觀察者
  • 演演算法可替換 → 策略
  • 固定流程可變步驟 → 模板方法
  • 多個處理者 → 責任鏈
  • 狀態決定行為 → 狀態

避免過度設計:不要為了使用設計模式而使用。當問題出現時再重構引入。