第一部分:設計模式基礎
1. 設計模式概述
設計模式(Design Pattern)是一套被反復使用、多數人知曉的、經過分類編目的代碼設計經驗的總結,它描述了在軟件設計過程中一些不斷重復出現的問題以及該問題的解決方案。設計模式是在特定環境下解決軟件設計問題的定制化方案,它不是可以直接轉化成代碼的模板,而是在特定情況下應用的指導原則。
- 提高代碼可維護性、可擴展性和復用性:
設計模式通過提供經過驗證的解決方案,幫助開發者編寫更清晰、更模塊化的代碼。例如,使用工廠模式可以降低代碼耦合度:
// 不使用工廠模式
public class Client {private Database db = new MySQLDatabase(); // 直接依賴具體實現public void doWork() {db.connect();// ...}
}// 使用工廠模式
public class Client {private Database db = DatabaseFactory.createDatabase(); // 依賴抽象public void doWork() {db.connect();// ...}
}
- 復雜業務場景下的優勢:
在復雜業務場景中,設計模式能有效管理對象間的交互和狀態變化。例如電商系統中的訂單狀態管理,使用狀態模式可以避免大量的條件判斷語句:
// 訂單狀態接口
public interface OrderState {void next(Order order);void previous(Order order);void printStatus();
}// 具體狀態:已下單
public class OrderedState implements OrderState {public void next(Order order) {order.setState(new PaidState());}public void previous(Order order) {System.out.println("訂單剛創建,沒有上一個狀態");}public void printStatus() {System.out.println("訂單已下單,等待支付");}
}// 訂單類
public class Order {private OrderState state;public Order() {this.state = new OrderedState();}public void nextState() {state.next(this);}// 其他方法...
}
- 新技術與設計模式的關系:
新技術往往建立在經典設計模式之上。例如:
- AOP(面向切面編程)基于代理模式實現
- Spring框架的依賴注入是工廠模式和策略模式的結合
- 響應式編程大量使用觀察者模式
// AOP中的代理模式示例
public interface UserService {void addUser(String name);
}public class UserServiceImpl implements UserService {public void addUser(String name) {System.out.println("添加用戶: " + name);}
}public class UserServiceProxy implements UserService {private UserService userService;public UserServiceProxy(UserService userService) {this.userService = userService;}public void addUser(String name) {System.out.println("開始事務...");userService.addUser(name);System.out.println("提交事務...");}
}
設計模式和架構模式在實際開發中常常結合使用。架構模式提供系統的整體結構,而設計模式則解決架構中各部分的實現細節問題。理解它們的區別和聯系,有助于開發者在不同層次上做出更合理的設計決策。
2. 面向對象設計原則
2.1 SOLID原則
- 單一職責原則(SRP):一個類只做一件事
// 反例 public class Employee {public void calculateSalary() { /* 計算薪資邏輯 */ }public void saveToDatabase() { /* 數據庫保存邏輯 */ }public void generateReport() { /* 生成報表邏輯 */ } }// 正例 public class Employee {public void calculateSalary() { /* 計算薪資邏輯 */ } }public class EmployeeRepository {public void save(Employee employee) { /* 數據庫保存邏輯 */ } }public class EmployeeReportGenerator {public void generate(Employee employee) { /* 生成報表邏輯 */ } }
- 開閉原則(OCP):軟件實體(類、模塊、函數等)對擴展開放,對修改關閉
// 反例 public class AreaCalculator {public double calculate(Object shape) {if (shape instanceof Rectangle) {// 計算矩形面積} else if (shape instanceof Circle) {// 計算圓形面積}// 每新增一種形狀都需要修改此類} }// 正例 public interface Shape {double calculateArea(); }public class Rectangle implements Shape {@Overridepublic double calculateArea() { /* 實現 */ } }public class Circle implements Shape {@Overridepublic double calculateArea() { /* 實現 */ } }public class AreaCalculator {public double calculate(Shape shape) {return shape.calculateArea();} }
- 里氏替換原則(LSP):子類可透明替換父類,不會引起任何錯誤
// 反例 public class Bird {public void fly() { /* 飛行實現 */ } }public class Penguin extends Bird {@Overridepublic void fly() {throw new UnsupportedOperationException("企鵝不會飛,但強制讓飛,報異常!");} }// 正例 public class Bird {// 基礎鳥類功能 }public class FlyingBird extends Bird {public void fly() { /* 飛行實現 */ } }public class Penguin extends Bird {// 企鵝特有功能 }
- 接口隔離原則(ISP):接口要小而專,客戶端不應該被迫依賴于它們不使用的接口
// 反例 public interface Worker {void work();void eat();void sleep(); }public class Robot implements Worker {public void work() { /* 工作 */ }public void eat() { /* 機器人不需要吃東西 */ }public void sleep() { /* 機器人不需要睡覺 */ } }// 正例 public interface Workable {void work(); }public interface Feedable {void eat();void sleep(); }public class Human implements Workable, Feedable {// 實現所有方法 }public class Robot implements Workable {// 只需要實現工作方法 }
- 依賴倒置原則(DIP):面向接口編程,高層模塊不應該依賴于低層模塊,二者都應該依賴于抽象
// 反例 public class LightBulb {public void turnOn() { /* 開燈 */ }public void turnOff() { /* 關燈 */ } }public class Switch {private LightBulb bulb;public void operate() {// 直接依賴具體實現if (bulb.isOn()) bulb.turnOff();else bulb.turnOn();} }// 正例 public interface Switchable {void turnOn();void turnOff(); }public class LightBulb implements Switchable {@Overridepublic void turnOn() { /* 實現 */ }@Overridepublic void turnOff() { /* 實現 */ } }public class Switch {private Switchable device;public void operate() {// 依賴抽象接口if (device.isOn()) device.turnOff();else device.turnOn();} }
2.2 其他重要原則
- 迪米特法則(LoD):最少知識原則
- 合成復用原則(CRP):優先使用組合而非繼承
- KISS原則:保持簡單
第二部分:創建型模式與應用
1. 單例模式
單例模式是Java中最常用的設計模式之一,它確保一個類只有一個實例,并提供一個全局訪問點。本文將全面解析單例模式的核心概念、實現方式、實際應用場景以及常見陷阱,并提供可直接在生產環境中使用的代碼示例。
1.1 核心概念與實現方式
- ?餓漢式、懶漢式、雙重檢查鎖、靜態內部類、枚舉
餓漢式單例
餓漢式單例在類加載時就創建實例,保證了線程安全,但可能造成資源浪費。
/*** 餓漢式單例實現* 優點:線程安全,實現簡單* 缺點:類加載時就初始化,可能造成資源浪費*/
public class EagerSingleton {// 類加載時就創建實例,保證線程安全private static final EagerSingleton INSTANCE = new EagerSingleton();// 私有構造函數防止外部實例化private EagerSingleton() {// 防止反射攻擊if (INSTANCE != null) {throw new IllegalStateException("單例實例已存在");}}// 提供全局訪問點public static EagerSingleton getInstance() {return INSTANCE;}// 示例方法public void doSomething() {System.out.println("餓漢式單例方法執行");}
}
懶漢式單例
懶漢式單例在第一次使用時才創建實例,節省了資源,但需要考慮線程安全問題。
/*** 懶漢式單例基礎實現(非線程安全)* 優點:延遲加載,節省資源* 缺點:非線程安全*/
public class LazySingleton {private static LazySingleton instance;private LazySingleton() {}public static LazySingleton getInstance() {if (instance == null) {instance = new LazySingleton();}return instance;}
}/*** 線程安全的懶漢式單例(同步方法)* 優點:線程安全,延遲加載* 缺點:每次獲取實例都需要同步,性能較差*/
public class ThreadSafeLazySingleton {private static ThreadSafeLazySingleton instance;private ThreadSafeLazySingleton() {}// 同步方法保證線程安全public static synchronized ThreadSafeLazySingleton getInstance() {if (instance == null) {instance = new ThreadSafeLazySingleton();}return instance;}
}
雙重檢查鎖定(DCL)單例
雙重檢查鎖定結合了懶漢式和餓漢式的優點,既實現了延遲加載,又保證了線程安全。
/*** 雙重檢查鎖定單例(推薦生產使用)* 優點:線程安全,延遲加載,性能較好* 注意:JDK5+才能保證完全正確,需要volatile關鍵字*/
public class DoubleCheckedLockingSingleton {// volatile保證可見性和禁止指令重排序private static volatile DoubleCheckedLockingSingleton instance;private DoubleCheckedLockingSingleton() {// 防止反射攻擊if (instance != null) {throw new IllegalStateException("單例實例已存在");}}public static DoubleCheckedLockingSingleton getInstance() {// 第一次檢查,避免不必要的同步if (instance == null) {synchronized (DoubleCheckedLockingSingleton.class) {// 第二次檢查,確保只有一個實例被創建if (instance == null) {instance = new DoubleCheckedLockingSingleton();}}}return instance;}
}
靜態內部類單例
靜態內部類方式實現了延遲加載和線程安全,且不需要同步。
/*** 靜態內部類實現單例(推薦生產使用)* 優點:線程安全,延遲加載,實現簡單* 原理:利用類加載機制保證線程安全*/
public class StaticInnerClassSingleton {// 私有構造函數private StaticInnerClassSingleton() {}// 靜態內部類private static class SingletonHolder {private static final StaticInnerClassSingleton INSTANCE = new StaticInnerClassSingleton();}public static StaticInnerClassSingleton getInstance() {return SingletonHolder.INSTANCE;}
}
枚舉單例
枚舉單例是《Effective Java》推薦的方式,簡潔且能防止反射和序列化攻擊。
/*** 枚舉實現單例(最佳實踐)* 優點:線程安全,防止反射和序列化攻擊,實現簡單* 推薦:生產環境首選方式*/
public enum EnumSingleton {INSTANCE;// 示例方法public void doSomething() {System.out.println("枚舉單例方法執行");}// 可以添加任意方法和屬性private String config;public String getConfig() {return config;}public void setConfig(String config) {this.config = config;}
}
- 線程安全問題與解決方案
- 餓漢式:天生線程安全,因為實例在類加載時創建?
- 懶漢式基礎版:非線程安全,多線程可能創建多個實例?
- 同步方法懶漢式:線程安全但性能差?
- 雙重檢查鎖定:線程安全且性能好,但實現較復雜?
- 靜態內部類:線程安全且實現簡單?
- 枚舉:最佳實踐,線程安全且防止反射攻擊?
1.2 實際應用場景
- 配置管理類
/*** 配置管理器單例實現* 適用于:全局配置信息管理*/ public class ConfigurationManager {private static final ConfigurationManager INSTANCE = new ConfigurationManager();private Properties configProps;private ConfigurationManager() {loadConfigurations();}public static ConfigurationManager getInstance() {return INSTANCE;}private void loadConfigurations() {configProps = new Properties();try (InputStream input = getClass().getClassLoader().getResourceAsStream("config.properties")) {if (input != null) {configProps.load(input);}} catch (IOException e) {throw new RuntimeException("加載配置文件失敗", e);}}public String getProperty(String key) {return configProps.getProperty(key);}public String getProperty(String key, String defaultValue) {return configProps.getProperty(key, defaultValue);} }
- 數據庫連接池
/*** 數據庫連接池單例實現* 適用于:管理數據庫連接資源*/ public class DatabaseConnectionPool {private static volatile DatabaseConnectionPool instance;private final List<Connection> connectionPool;private final int MAX_POOL_SIZE = 10;private DatabaseConnectionPool() {// 初始化連接池connectionPool = new ArrayList<>(MAX_POOL_SIZE);initializePool();}public static DatabaseConnectionPool getInstance() {if (instance == null) {synchronized (DatabaseConnectionPool.class) {if (instance == null) {instance = new DatabaseConnectionPool();}}}return instance;}private void initializePool() {try {for (int i = 0; i < MAX_POOL_SIZE; i++) {// 這里應該使用真實的數據庫連接配置Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "user", "password");connectionPool.add(conn);}} catch (SQLException e) {throw new RuntimeException("初始化數據庫連接池失敗", e);}}public synchronized Connection getConnection() {if (connectionPool.isEmpty()) {throw new RuntimeException("連接池已耗盡");}return connectionPool.remove(connectionPool.size() - 1);}public synchronized void releaseConnection(Connection conn) {if (conn != null) {connectionPool.add(conn);}} }
- 日志處理器
/*** 日志處理器單例實現* 適用于:集中管理應用日志*/ public class Logger {private static final Logger INSTANCE = new Logger();private final Queue<String> logQueue;private final int MAX_QUEUE_SIZE = 1000;private final Thread logThread;private volatile boolean running = true;private Logger() {logQueue = new LinkedList<>();logThread = new Thread(this::processLogs);logThread.start();// 添加JVM關閉鉤子Runtime.getRuntime().addShutdownHook(new Thread(() -> {running = false;try {logThread.join();} catch (InterruptedException e) {Thread.currentThread().interrupt();}}));}public static Logger getInstance() {return INSTANCE;}public void log(String message) {synchronized (logQueue) {if (logQueue.size() >= MAX_QUEUE_SIZE) {logQueue.poll(); // 移除最舊的消息}logQueue.offer(message);logQueue.notify();}}private void processLogs() {while (running || !logQueue.isEmpty()) {String message;synchronized (logQueue) {while (logQueue.isEmpty() && running) {try {logQueue.wait(1000);} catch (InterruptedException e) {Thread.currentThread().interrupt();}}message = logQueue.poll();}if (message != null) {// 實際應用中應該寫入文件或發送到日志服務器System.out.println("[LOG] " + message);}}} }
- Spring中的單例Bean
/*** Spring中的單例Bean示例* 使用注解方式聲明單例Bean*/ @Service // 等同于@Component,但更明確表示服務層 public class OrderService {@Autowiredprivate OrderRepository orderRepository;public Order createOrder(Order order) {// 業務邏輯處理return orderRepository.save(order);} }/*** 配置類中聲明單例Bean*/ @Configuration public class AppConfig {@Beanpublic DataSource dataSource() {// 創建并返回數據源實例// 默認是單例的,整個應用共享同一個實例return new HikariDataSource();} }
Spring框架默認使用單例作用域管理Bean,但不同于傳統單例模式,Spring的單例是相對于IoC容器而言的。Spring單例Bean的特點:
1.默認作用域就是單例,無需特殊配置
2.由Spring容器管理生命周期,不同于傳統單例模式
3.支持依賴注入,更符合現代應用架構
4.可以通過@Scope("singleton")顯式聲明,但通常不需要
1.3 反模式與陷阱
- ?單例導致的內存泄漏
單例對象如果持有外部資源的引用而不釋放,會導致內存泄漏
/*** 可能導致內存泄漏的單例示例*/
public class LeakySingleton {private static LeakySingleton instance;private Map<String, Object> cache = new HashMap<>();private LeakySingleton() {}public static synchronized LeakySingleton getInstance() {if (instance == null) {instance = new LeakySingleton();}return instance;}// 向緩存添加數據,但從不清理public void addToCache(String key, Object value) {cache.put(key, value);}// 問題:緩存會無限增長,導致內存泄漏
}
解決方案:
- 對緩存設置大小限制或過期策略
- 提供清理緩存的方法
- 使用WeakReference存儲緩存對象
/*** 改進后的安全單例緩存*/
public class SafeCacheSingleton {private static SafeCacheSingleton instance;private final Map<String, SoftReference<Object>> cache;private final int MAX_CACHE_SIZE = 1000;private SafeCacheSingleton() {cache = new LinkedHashMap<String, SoftReference<Object>>(MAX_CACHE_SIZE, 0.75f, true) {@Overrideprotected boolean removeEldestEntry(Map.Entry<String, SoftReference<Object>> eldest) {return size() > MAX_CACHE_SIZE;}};}public static synchronized SafeCacheSingleton getInstance() {if (instance == null) {instance = new SafeCacheSingleton();}return instance;}public void addToCache(String key, Object value) {synchronized (cache) {cache.put(key, new SoftReference<>(value));}}public Object getFromCache(String key) {synchronized (cache) {SoftReference<Object> ref = cache.get(key);return ref != null ? ref.get() : null;}}public void clearCache() {synchronized (cache) {cache.clear();}}
}
- 過度使用單例的問題
- 測試困難:單例狀態在測試間持久化,導致測試相互影響?
- 隱藏的依賴:單例作為全局變量,使依賴關系不明確?
- 違反單一職責原則:單例類往往承擔過多職責?
- 并發問題:雖然單例本身線程安全,但其內部狀態可能需要額外同步?
/*** 職責單一的單例示例*/
public class IdGenerator {private static final IdGenerator INSTANCE = new IdGenerator();private final AtomicLong counter = new AtomicLong(0);private IdGenerator() {}public static IdGenerator getInstance() {return INSTANCE;}// 唯一職責:生成IDpublic long nextId() {return counter.incrementAndGet();}
}
- 總結與最佳實踐
實現方式選擇:
- 簡單場景:枚舉單例(最佳選擇)?
- 需要延遲加載:靜態內部類方式?
- 復雜初始化:雙重檢查鎖定?
線程安全:
- 確保單例創建過程線程安全?
- 注意單例內部狀態的線程安全?
生產環境注意事項:
- 防止反射攻擊:在構造函數中添加檢查?
- 處理序列化:實現readResolve方法或使用枚舉?
- 考慮內存泄漏:合理管理單例持有的資源?
替代方案:
- 在Spring等現代框架中,優先使用依賴注入?
- 考慮使用框架提供的單例管理能力?
通過合理使用單例模式,可以有效地管理共享資源,提高系統性能,但務必注意避免過度使用和潛在陷阱。
2. 工廠模式家族
2.1 簡單工廠模式
簡單工廠模式(Simple Factory Pattern)又稱為靜態工廠方法模式(Static Factory Method Pattern),它定義一個工廠類,根據傳入的參數不同返回不同類的實例,被創建的實例通常都具有共同的父類。
- 靜態工廠方法的實現
// 抽象產品接口
public interface Chart {void display();
}// 具體產品類:柱狀圖
public class HistogramChart implements Chart {@Overridepublic void display() {System.out.println("顯示柱狀圖");}
}// 具體產品類:餅狀圖
public class PieChart implements Chart {@Overridepublic void display() {System.out.println("顯示餅狀圖");}
}// 圖表工廠類
public class ChartFactory {// 靜態工廠方法public static Chart getChart(String type) {if ("histogram".equalsIgnoreCase(type)) {return new HistogramChart();} else if ("pie".equalsIgnoreCase(type)) {return new PieChart();}throw new IllegalArgumentException("Unsupported chart type");}
}// 客戶端使用
public class Client {public static void main(String[] args) {Chart chart = ChartFactory.getChart("pie");chart.display(); // 輸出: 顯示餅狀圖}
}
- 適用場景與局限性
適用場景:
- 工廠類負責創建的對象比較少,客戶端只需要傳入工廠類的參數,不需要關心對象創建的細節?
- 需要集中管理對象的創建邏輯,避免創建邏輯分散在代碼各處
- 需要對客戶端隱藏具體實現類,降低耦合度
局限性:
- 違反開閉原則:增加新產品時需要修改工廠類邏輯?
- 工廠類職責過重,隨著產品增多會變得臃腫復雜
- 使用靜態方法導致工廠類無法通過繼承來擴展?
典型應用案例:
- JDK中的
Calendar.getInstance()
、NumberFormat.getInstance()
等?- 日志框架中的LoggerFactory.getLogger()
- Spring框架中的BeanFactory.getBean()方法族?
2.2 工廠方法模式
工廠方法模式(Factory Method Pattern)定義了一個創建對象的接口,但讓子類決定實例化哪一個類。工廠方法使一個類的實例化延遲到其子類。
模式結構:
- 抽象創建者:聲明工廠方法,返回抽象產品類型
- 具體創建者:實現工廠方法,返回具體產品實例
- 抽象產品:定義產品的接口
- 具體產品:實現抽象產品接口的具體類
- 抽象創建者與具體創建者
// 抽象產品 public interface Logger {void log(String message); }// 具體產品 public class FileLogger implements Logger {@Overridepublic void log(String message) {System.out.println("Log to file: " + message);} }public class ConsoleLogger implements Logger {@Overridepublic void log(String message) {System.out.println("Log to console: " + message);} }// 抽象創建者 public abstract class LoggerFactory {public abstract Logger createLogger();public void log(String message) {Logger logger = createLogger();logger.log(message);} }// 具體創建者 public class FileLoggerFactory extends LoggerFactory {@Overridepublic Logger createLogger() {return new FileLogger();} }public class ConsoleLoggerFactory extends LoggerFactory {@Overridepublic Logger createLogger() {return new ConsoleLogger();} }
- JDK中的工廠方法應用
集合框架中的迭代器:
public interface Collection<E> {Iterator<E> iterator(); // 工廠方法
}public class ArrayList<E> implements Collection<E> {public Iterator<E> iterator() {return new Itr(); // 具體產品}private class Itr implements Iterator<E> { /*...*/ }
}
?URLStreamHandlerFactory:
URL.setURLStreamHandlerFactory(new URLStreamHandlerFactory() {public URLStreamHandler createURLStreamHandler(String protocol) {if ("myproto".equals(protocol)) {return new MyURLStreamHandler(); // 具體產品}return null;}
});
2.3 抽象工廠模式
抽象工廠模式(Abstract Factory Pattern)提供一個創建一系列相關或相互依賴對象的接口,而無需指定它們具體的類。
- 產品族與產品等級結構
核心概念:
- 產品等級結構:產品的繼承結構,如抽象電視機與海爾電視機、TCL電視機等具體品牌電視機構成的產品等級結構?
- 產品族:由同一個工廠生產的,位于不同產品等級結構中的一組產品,如海爾電器工廠生產的海爾電視機、海爾電冰箱構成一個產品族?
- ?跨平臺UI組件庫實現
下面是一個跨平臺UI組件庫的抽象工廠實現:?
// 抽象產品:按鈕
public interface Button {void render();void onClick();
}// 具體產品:Windows按鈕
public class WindowsButton implements Button {public void render() {System.out.println("Render Windows style button");}public void onClick() {System.out.println("Windows button clicked");}
}// 具體產品:MacOS按鈕
public class MacOSButton implements Button {public void render() {System.out.println("Render MacOS style button");}public void onClick() {System.out.println("MacOS button clicked");}
}// 抽象產品:復選框
public interface Checkbox {void render();void onCheck();
}// 具體產品:Windows復選框
public class WindowsCheckbox implements Checkbox {public void render() {System.out.println("Render Windows style checkbox");}public void onCheck() {System.out.println("Windows checkbox checked");}
}// 具體產品:MacOS復選框
public class MacOSCheckbox implements Checkbox {public void render() {System.out.println("Render MacOS style checkbox");}public void onCheck() {System.out.println("MacOS checkbox checked");}
}// 抽象工廠
public interface GUIFactory {Button createButton();Checkbox createCheckbox();
}// 具體工廠:Windows工廠
public class WindowsFactory implements GUIFactory {public Button createButton() {return new WindowsButton();}public Checkbox createCheckbox() {return new WindowsCheckbox();}
}// 具體工廠:MacOS工廠
public class MacOSFactory implements GUIFactory {public Button createButton() {return new MacOSButton();}public Checkbox createCheckbox() {return new MacOSCheckbox();}
}// 客戶端代碼
public class Application {private Button button;private Checkbox checkbox;public Application(GUIFactory factory) {button = factory.createButton();checkbox = factory.createCheckbox();}public void renderUI() {button.render();checkbox.render();}
}
2.4 實際應用案例
?2.4.1.BeanFactory
Spring框架的核心容器BeanFactory是工廠模式的典型應用,它通過工廠方法模式管理Bean的生命周期。
核心接口:
public interface BeanFactory {Object getBean(String name) throws BeansException;<T> T getBean(String name, Class<T> requiredType) throws BeansException;Object getBean(String name, Object... args) throws BeansException;// 其他方法...
}
實現原理:
- 工廠方法:
getBean()
是核心工廠方法 - 產品族管理:可以管理單例、原型等不同作用域的Bean
- 延遲加載:支持懶加載模式,優化啟動性能
擴展實現:
public class InstanceFactory {private static final Map<String, Object> beanMap = new HashMap<>();static {// 初始化BeanbeanMap.put("userService", new UserServiceImpl());beanMap.put("orderService", new OrderServiceImpl());}public static Object getBean(String name) {return beanMap.get(name);}
}
Spring的BeanFactory比簡單工廠更強大:
- 支持依賴注入
- 管理Bean的生命周期
- 提供AOP等高級特性?
2.4.2.JDBC驅動加載機制
JDBC驅動加載機制是工廠方法模式的應用,DriverManager
作為抽象工廠,各數據庫廠商提供具體驅動實現。
驅動加載流程:
// 1. 加載驅動類 (靜態工廠方法)
Class.forName("com.mysql.jdbc.Driver");// 2. 獲取連接 (工廠方法)
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "user", "password");// 3. 創建Statement
Statement stmt = conn.createStatement();// 4. 執行查詢
ResultSet rs = stmt.executeQuery("SELECT * FROM users");
工廠模式體現:
DriverManager.getConnection()
是工廠方法- 不同數據庫廠商提供自己的
Driver
實現 - 客戶端通過統一接口使用不同數據庫
2.4.3.Appender工廠
Log4j等日志框架使用抽象工廠模式創建不同的Appender(輸出目的地)。
配置示例:
log4j.rootLogger=DEBUG, console, file# 控制臺Appender
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n# 文件Appender
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=C:/logs/app.log
log4j.appender.file.MaxFileSize=10MB
工廠實現:
public interface Appender {void append(LoggingEvent event);
}public class ConsoleAppender implements Appender {public void append(LoggingEvent event) {System.out.println(event.getMessage());}
}public class FileAppender implements Appender {private String filePath;public FileAppender(String filePath) {this.filePath = filePath;}public void append(LoggingEvent event) {// 寫入文件的實現}
}public class AppenderFactory {public static Appender createAppender(String appenderName, Properties props) {if ("console".equals(appenderName)) {return new ConsoleAppender();} else if ("file".equals(appenderName)) {String filePath = props.getProperty("log4j.appender.file.File");return new FileAppender(filePath);}throw new IllegalArgumentException("Unknown appender: " + appenderName);}
}
日志框架通過工廠模式可以靈活配置輸出目的地,而不需要修改業務代碼。
2.5.工廠模式總結對比
模式 | 特點 | 優點 | 缺點 | 適用場景 |
---|---|---|---|---|
簡單工廠 | 一個工廠類,靜態方法創建產品 | 封裝創建邏輯,客戶端與產品解耦 | 違反開閉原則,工廠類職責過重 | 創建對象較少,不頻繁變化的場景? 53 |
工廠方法 | 抽象工廠類,子類決定創建何種產品 | 符合開閉原則,支持擴展 | 每增加產品需增加工廠類 | 不明確知道創建何種對象的場景? 60 |
抽象工廠 | 創建多個產品族的對象 | 保證產品兼容性,易于交換產品系列 | 難以支持新種類產品 | 需要創建相關或依賴對象的系列? 21 |
在實際開發中,應根據業務需求選擇合適的工廠模式:
- 簡單工廠:適用于對象創建邏輯簡單的場景
- 工廠方法:適用于需要靈活擴展的場景
- 抽象工廠:適用于需要保證產品兼容性的復雜場景?
工廠模式是Java開發中最常用的設計模式之一,合理運用可以大大提高代碼的可維護性和擴展性。
3. 建造者模式
5.1 模式結構與實現
- 角色定義與UML類圖
建造者模式(Builder Pattern)是一種創建型設計模式,它將復雜對象的構建與其表示分離,使得同樣的構建過程可以創建不同的表示。該模式主要包含以下4個核心角色:
- Director(指揮者):負責調用具體建造者來構建產品對象,它知道構建順序但不知道具體構建細節
- Builder(抽象建造者):定義構建產品各個部件的抽象接口?
- ConcreteBuilder(具體建造者):實現Builder接口,提供具體構建方法和返回產品的方法?
- Product(產品):被構建的復雜對象,包含多個組成部件
- ?標準實現示例:
產品類(Product)
public class Computer {private final String cpu; // 必須參數private final String ram; // 必須參數private final int usbCount; // 可選參數private final String keyboard; // 可選參數private final String display; // 可選參數// 私有構造方法,只能通過Builder構建private Computer(Builder builder) {this.cpu = builder.cpu;this.ram = builder.ram;this.usbCount = builder.usbCount;this.keyboard = builder.keyboard;this.display = builder.display;}// 靜態Builder類public static class Builder {private String cpu; // 必須參數private String ram; // 必須參數private int usbCount; // 可選參數,默認值private String keyboard = "默認鍵盤"; // 可選參數,默認值private String display = "默認顯示器"; // 可選參數,默認值// 必須參數的構造方法public Builder(String cpu, String ram) {this.cpu = cpu;this.ram = ram;}// 可選參數的設置方法,返回Builder實現鏈式調用public Builder setUsbCount(int usbCount) {this.usbCount = usbCount;return this;}public Builder setKeyboard(String keyboard) {this.keyboard = keyboard;return this;}public Builder setDisplay(String display) {this.display = display;return this;}// 構建方法public Computer build() {return new Computer(this);}}
}
指揮者類(Director)
public class ComputerDirector {public Computer constructGamingComputer() {return new Computer.Builder("Intel i9", "32GB").setUsbCount(4).setKeyboard("機械鍵盤").setDisplay("4K 144Hz").build();}public Computer constructOfficeComputer() {return new Computer.Builder("Intel i5", "16GB").setUsbCount(2).setKeyboard("薄膜鍵盤").build();}
}
客戶端調用
public class Client {public static void main(String[] args) {// 使用Builder直接構建Computer gamingComputer = new Computer.Builder("AMD Ryzen 9", "32GB").setUsbCount(6).setKeyboard("RGB機械鍵盤").setDisplay("2K 240Hz").build();// 使用Director構建預設配置ComputerDirector director = new ComputerDirector();Computer officeComputer = director.constructOfficeComputer();Computer highEndComputer = director.constructGamingComputer();}
}
鏈式調用實現原理:
鏈式調用的核心在于每個setter方法都返回Builder對象本身(this),使得可以連續調用多個方法。這種編碼風格使代碼更加簡潔易讀,也是建造者模式的常見實現方式。
5.2 實際應用場景
建造者模式特別適用于以下場景:
- 參數多且有可選參數:當一個類的構造函數參數超過4個,且很多參數是可選的
- 參數之間有依賴關系:某些參數必須在其他參數設置后才能設置
- 對象不可變:構建完成后對象狀態不應再改變
- 多種配置變體:需要創建具有不同配置的同一類對象
電商訂單系統示例:
public class Order {private final String orderId;private final Long userId;private final List<OrderItem> items;private final PaymentInfo paymentInfo;private final ShippingInfo shippingInfo;private final CouponInfo couponInfo;private final Date createTime;private Order(Builder builder) {this.orderId = builder.orderId;this.userId = builder.userId;this.items = builder.items;this.paymentInfo = builder.paymentInfo;this.shippingInfo = builder.shippingInfo;this.couponInfo = builder.couponInfo;this.createTime = builder.createTime;}public static class Builder {private String orderId;private Long userId;private List<OrderItem> items = new ArrayList<>();private PaymentInfo paymentInfo;private ShippingInfo shippingInfo;private CouponInfo couponInfo;private Date createTime = new Date();public Builder(String orderId, Long userId) {this.orderId = orderId;this.userId = userId;}public Builder addItem(OrderItem item) {this.items.add(item);return this;}public Builder setPaymentInfo(PaymentInfo paymentInfo) {this.paymentInfo = paymentInfo;return this;}public Builder setShippingInfo(ShippingInfo shippingInfo) {this.shippingInfo = shippingInfo;return this;}public Builder applyCoupon(CouponInfo couponInfo) {this.couponInfo = couponInfo;return this;}public Order build() {// 可以在此處添加校驗邏輯if (items.isEmpty()) {throw new IllegalStateException("訂單必須包含至少一件商品");}if (paymentInfo == null) {throw new IllegalStateException("必須設置支付信息");}return new Order(this);}}
}
Lombok的@Builder注解
Lombok的@Builder注解可以自動生成建造者模式的代碼,大大簡化開發:
import lombok.Builder;
import lombok.ToString;@Builder
@ToString
public class User {private String username;private String password;private int age;private String email;private String phone;
}// 使用方式
public class Main {public static void main(String[] args) {User user = User.builder().username("john_doe").password("secure123").age(30).email("john@example.com").phone("1234567890").build();System.out.println(user);}
}
編譯后的代碼實際上會生成類似于以下的結構:
public class User {private String username;private String password;// 其他字段...User(String username, String password, int age, String email, String phone) {this.username = username;this.password = password;this.age = age;this.email = email;this.phone = phone;}public static UserBuilder builder() {return new UserBuilder();}public static class UserBuilder {private String username;private String password;private int age;private String email;private String phone;UserBuilder() {}public UserBuilder username(String username) {this.username = username;return this;}// 其他setter方法...public User build() {return new User(username, password, age, email, phone);}}
}
MyBatis的SqlSessionFactoryBuilder
MyBatis使用SqlSessionFactoryBuilder來構建SqlSessionFactory:
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);// 或者使用Java配置方式
DataSource dataSource = BlogDataSourceFactory.getBlogDataSource();
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment("development", transactionFactory, dataSource);
Configuration configuration = new Configuration(environment);
configuration.addMapper(BlogMapper.class);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
SqlSessionFactoryBuilder的內部實現簡化版:
public class SqlSessionFactoryBuilder {public SqlSessionFactory build(InputStream inputStream) {try {XMLConfigBuilder parser = new XMLConfigBuilder(inputStream);return build(parser.parse());} catch (Exception e) {throw ExceptionFactory.wrapException("Error building SqlSession.", e);} finally {ErrorContext.instance().reset();try {inputStream.close();} catch (IOException e) {// Intentionally ignore. Prefer previous error.}}}public SqlSessionFactory build(Configuration config) {return new DefaultSqlSessionFactory(config);}
}
5.3.建造者模式的最佳實踐
- 將Builder作為靜態內部類:這是最常用的實現方式,可以訪問外部類的私有構造方法?
- 參數校驗:在build()方法中進行參數校驗,確保對象的有效性?
- 不可變對象:建造者模式特別適合創建不可變對象,因為所有參數都可以在構造時一次性設置?
- 與工廠模式結合:對于特別復雜的對象,可以結合工廠方法模式來創建不同的Builder?
- 方法鏈設計:每個setter方法都返回Builder對象本身,支持鏈式調用?
建造者模式通過將復雜對象的構建過程分解為多個步驟,并允許通過不同的具體建造者實現不同的構建過程,提供了極大的靈活性。同時,它將客戶端與復雜對象的構建過程解耦,使得客戶端無需知道具體的構建細節,只需指定需要構建的類型即可。
4. 原型模式
4.1 淺拷貝與深拷貝
4.1.1.基本概念與區別
原型模式(Prototype Pattern)是一種創建型設計模式,它通過復制現有對象來創建新對象,而不是通過new操作符。在Java中,拷貝分為三種類型:
- 淺拷貝(Shallow Copy):只復制對象本身,不復制對象引用的其他對象。新對象和原對象共享引用對象的同一內存地址 。
- 深拷貝(Deep Copy):不僅復制對象本身,還復制對象包含的所有子對象。新對象和原對象完全獨立 。
- 引用拷貝(Reference Copy):僅復制對象的引用,新舊對象指向同一個內存地址 。
4.1.2.clone()方法的實現
Java中實現拷貝需要以下步驟:
- 實現
Cloneable
接口(標記接口) - 重寫
Object
類的clone()
方法?
淺拷貝示例:
public class Student implements Cloneable {private String name;private int age;private Date birthDate; // 引用類型// 構造方法和其他方法省略@Overridepublic Object clone() throws CloneNotSupportedException {return super.clone(); // 淺拷貝}
}
深拷貝實現方式:
- 遞歸調用clone方法:
@Override
public Object clone() throws CloneNotSupportedException {Student cloned = (Student)super.clone();cloned.birthDate = (Date)this.birthDate.clone(); // 對引用類型也調用clonereturn cloned;
}
- 通過構造方法實現深拷貝:
public Student(Student other) {this.name = other.name;this.age = other.age;this.birthDate = new Date(other.birthDate.getTime());
}
4.1.3.序列化實現深拷貝
當對象圖較復雜時,手動實現深拷貝會很繁瑣,可以使用序列化機制:
import java.io.*;public class DeepCopyUtil {@SuppressWarnings("unchecked")public static <T extends Serializable> T deepCopy(T object) {try {ByteArrayOutputStream bos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(bos);oos.writeObject(object);oos.flush();ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());ObjectInputStream ois = new ObjectInputStream(bis);return (T) ois.readObject();} catch (Exception e) {throw new RuntimeException("Deep copy failed", e);}}
}
使用條件:
- 所有涉及的對象都必須實現
Serializable
接口 - 性能比直接clone方法差,但實現簡單?
4.2 實際應用場景
4.2.1.對象創建成本高
原型模式特別適用于以下高成本對象創建場景:
- 需要復雜計算初始化:如從數據庫加載大量數據、復雜數學計算等
- 需要復雜配置:如需要多個步驟配置的對象
- 需要訪問遠程資源:如從網絡或文件系統加載數據
示例代碼:
public class ExpensiveObject implements Cloneable {private List<String> heavyData;private Configuration config;public ExpensiveObject() {// 耗時的初始化過程this.heavyData = loadHeavyDataFromDB(); // 可能耗時this.config = initComplexConfig(); // 復雜配置}// 原型模式優化創建public ExpensiveObject createFromPrototype() {try {ExpensiveObject copy = (ExpensiveObject)this.clone();// 可能需要重置某些狀態return copy;} catch (CloneNotSupportedException e) {throw new RuntimeException("Clone not supported", e);}}
}
4.2.2.撤銷操作歷史記錄
原型模式可以用于實現撤銷(Undo)功能:
- 保存對象的歷史狀態作為"備忘錄"
- 需要撤銷時,從歷史狀態恢復
實現代碼:
public class Document implements Cloneable {private String content;private List<String> images;// 創建備忘錄(使用原型模式)public Document createMemento() {try {Document memento = (Document)this.clone();// 深拷貝可變字段memento.images = new ArrayList<>(this.images);return memento;} catch (CloneNotSupportedException e) {throw new RuntimeException("Clone failed", e);}}// 從備忘錄恢復public void restoreFromMemento(Document memento) {this.content = memento.content;this.images = new ArrayList<>(memento.images);}
}public class DocumentHistory {private Stack<Document> history = new Stack<>();public void save(Document doc) {history.push(doc.createMemento());}public Document undo() {if (!history.isEmpty()) {return history.pop();}return null;}
}
4.2.3.Spring原型Bean
在Spring框架中,原型(Prototype)作用域的Bean每次獲取時都會創建新實例:
配置原型Bean:
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class PrototypeBean {private static int instanceCount = 0;public PrototypeBean() {instanceCount++;System.out.println("PrototypeBean instance created: " + instanceCount);}
}
獲取原型Bean的方式:
- 使用ApplicationContext:
@Autowired
private ApplicationContext applicationContext;public void usePrototype() {PrototypeBean bean = applicationContext.getBean(PrototypeBean.class);
}
- 使用@Lookup注解:
@RestController
public class MyController {@GetMapping("/bean")public String getBean() {PrototypeBean bean = createPrototypeBean();return "Prototype bean: " + bean.hashCode();}@Lookupprotected PrototypeBean createPrototypeBean() {return null; // 由Spring實現}
}
- 使用ObjectProvider:
@Autowired
private ObjectProvider<PrototypeBean> prototypeBeanProvider;public void usePrototype() {PrototypeBean bean = prototypeBeanProvider.getObject();
}
原型Bean的典型應用場景:
- 每次需要新實例的場景
- 有狀態的Bean
- 需要隔離不同請求的Bean
- Builder模式中保持配置但創建不同對象
4.2.4.最佳實踐
深拷貝的權衡:
- 完全深拷貝幾乎不可能實現,因為對象可能包含循環引用?
- 根據實際需求決定拷貝深度
- 不可變對象(如String)不需要深拷貝?
性能考慮:
- 原型模式比直接創建對象快,特別是大對象?
- 序列化方式比clone()方法慢?
設計建議:
- 考慮使用"拷貝構造器"或"拷貝工廠"代替Cloneable?
- 對于復雜對象,結合原型模式和建造者模式?
Spring中的使用技巧:
- 原型Bean與@Autowired一起使用時要小心,因為注入只發生一次?
- 對于有依賴的原型Bean,最好使用方法注入?
原型模式通過復制現有對象來創建新對象,避免了昂貴的創建過程,是創建型模式中非常有價值的一種。正確使用原型模式可以顯著提高性能,特別是在需要創建大量相似對象的場景中。
第三部分:結構型模式與應用
1. 適配器模式
1.1 類適配器與對象適配器
適配器模式是將一個類的接口轉換成客戶希望的另外一個接口,使得原本由于接口不兼容而不能一起工作的類可以一起工作。
1.1.1.繼承與組合
類適配器(通過繼承實現):
// 目標接口
interface Target {void request();
}// 被適配者
class Adaptee {public void specificRequest() {System.out.println("被適配者的方法");}
}// 類適配器
class ClassAdapter extends Adaptee implements Target {@Overridepublic void request() {specificRequest();}
}
對象適配器(通過組合實現):
// 對象適配器
class ObjectAdapter implements Target {private Adaptee adaptee;public ObjectAdapter(Adaptee adaptee) {this.adaptee = adaptee;}@Overridepublic void request() {adaptee.specificRequest();}
}// 使用示例
public class AdapterDemo {public static void main(String[] args) {// 類適配器Target classAdapter = new ClassAdapter();classAdapter.request();// 對象適配器Adaptee adaptee = new Adaptee();Target objectAdapter = new ObjectAdapter(adaptee);objectAdapter.request();}
}
1.1.2.第三方庫適配
// 假設我們有一個第三方支付接口
interface ThirdPartyPayment {void pay(double amount);
}// 我們系統的支付接口
interface PaymentService {void makePayment(double amount, String currency);
}// 適配器
class PaymentAdapter implements PaymentService {private ThirdPartyPayment thirdPartyPayment;public PaymentAdapter(ThirdPartyPayment thirdPartyPayment) {this.thirdPartyPayment = thirdPartyPayment;}@Overridepublic void makePayment(double amount, String currency) {// 轉換貨幣為美元(假設第三方只接受美元)if (!"USD".equals(currency)) {amount = convertCurrency(amount, currency);}thirdPartyPayment.pay(amount);}private double convertCurrency(double amount, String currency) {// 實際項目中這里會調用匯率轉換服務return amount * 0.85; // 簡單示例}
}
1.2 實際應用場景
1.2.1.JDBC驅動適配不同數據庫
// JDBC本身就是適配器模式的典型應用
public class JdbcAdapterExample {public static void main(String[] args) {// 同樣的接口,不同的數據庫驅動Connection mysqlConn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test");Connection oracleConn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:orcl");// 使用相同的接口操作不同的數據庫Statement mysqlStmt = mysqlConn.createStatement();Statement oracleStmt = oracleConn.createStatement();}
}
1.2.2.SLF4J日志門面
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class Slf4jExample {private static final Logger logger = LoggerFactory.getLogger(Slf4jExample.class);public static void main(String[] args) {// 同樣的日志接口,底層可以適配Log4j、Logback、JUL等不同實現logger.info("This is an info message");logger.error("This is an error message");}
}
1.2.3.Spring MVC中的HandlerAdapter
// Spring MVC中的HandlerAdapter接口
public interface HandlerAdapter {boolean supports(Object handler);ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;long getLastModified(HttpServletRequest request, Object handler);
}// 實際應用中,Spring會根據不同的Controller類型選擇不同的適配器
2. 裝飾器模式
2.1 動態添加職責
裝飾器模式允許向一個現有的對象添加新的功能,同時又不改變其結構。
2.1.1.透明裝飾與半透明裝飾
透明裝飾(保持接口一致):
// 組件接口
interface Coffee {double getCost();String getDescription();
}// 具體組件
class SimpleCoffee implements Coffee {@Overridepublic double getCost() {return 1.0;}@Overridepublic String getDescription() {return "Simple coffee";}
}// 裝飾器基類(透明裝飾)
abstract class CoffeeDecorator implements Coffee {protected final Coffee decoratedCoffee;public CoffeeDecorator(Coffee coffee) {this.decoratedCoffee = coffee;}public double getCost() {return decoratedCoffee.getCost();}public String getDescription() {return decoratedCoffee.getDescription();}
}// 具體裝飾器
class MilkDecorator extends CoffeeDecorator {public MilkDecorator(Coffee coffee) {super(coffee);}@Overridepublic double getCost() {return super.getCost() + 0.5;}@Overridepublic String getDescription() {return super.getDescription() + ", with milk";}
}class SugarDecorator extends CoffeeDecorator {public SugarDecorator(Coffee coffee) {super(coffee);}@Overridepublic double getCost() {return super.getCost() + 0.2;}@Overridepublic String getDescription() {return super.getDescription() + ", with sugar";}
}
2.1.2.半透明裝飾
// 半透明裝飾器
class WhipCreamDecorator extends CoffeeDecorator {public WhipCreamDecorator(Coffee coffee) {super(coffee);}// 新增方法public String getExtraTopping() {return "Whipped cream";}@Overridepublic double getCost() {return super.getCost() + 0.7;}@Overridepublic String getDescription() {return super.getDescription() + ", with whipped cream";}
}// 使用示例
public class DecoratorDemo {public static void main(String[] args) {// 透明裝飾Coffee coffee = new SimpleCoffee();coffee = new MilkDecorator(coffee);coffee = new SugarDecorator(coffee);System.out.println(coffee.getDescription() + " costs $" + coffee.getCost());// 半透明裝飾Coffee specialCoffee = new SimpleCoffee();specialCoffee = new WhipCreamDecorator(specialCoffee);System.out.println(((WhipCreamDecorator)specialCoffee).getExtraTopping());}
}
2.1.3.與繼承的區別
- 裝飾器模式是動態的,可以在運行時添加或移除功能
- 繼承是靜態的,在編譯時就已經確定
- 裝飾器模式可以組合多個功能,而繼承會導致類爆炸
2.2 實際應用場景
2.2.1.Java I/O流體系
public class IoDecoratorExample {public static void main(String[] args) throws IOException {// FileInputStream是具體組件InputStream inputStream = new FileInputStream("test.txt");// BufferedInputStream是裝飾器inputStream = new BufferedInputStream(inputStream);// DataInputStream是另一個裝飾器DataInputStream dataInputStream = new DataInputStream(inputStream);// 可以這樣組合使用DataInputStream dis = new DataInputStream(new BufferedInputStream(new FileInputStream("test.txt")));}
}
2.2.2.Servlet API中的HttpServletRequestWrapper
public class LoggingRequestWrapper extends HttpServletRequestWrapper {public LoggingRequestWrapper(HttpServletRequest request) {super(request);}@Overridepublic String getParameter(String name) {String value = super.getParameter(name);System.out.println("Request parameter: " + name + "=" + value);return value;}
}// 在Filter中使用
public class LoggingFilter implements Filter {@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {HttpServletRequest httpRequest = (HttpServletRequest) request;LoggingRequestWrapper wrapper = new LoggingRequestWrapper(httpRequest);chain.doFilter(wrapper, response);}
}
2.2.3.Spring Cache中的緩存裝飾
@Configuration
@EnableCaching
public class CacheConfig extends CachingConfigurerSupport {@Beanpublic CacheManager cacheManager() {ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager() {@Overrideprotected Cache createConcurrentMapCache(String name) {// 裝飾原始緩存,添加日志功能return new LoggingCacheWrapper(super.createConcurrentMapCache(name));}};return cacheManager;}
}class LoggingCacheWrapper implements Cache {private final Cache delegate;public LoggingCacheWrapper(Cache delegate) {this.delegate = delegate;}@Overridepublic String getName() {return delegate.getName();}@Overridepublic Object getNativeCache() {return delegate.getNativeCache();}@Overridepublic ValueWrapper get(Object key) {System.out.println("Getting from cache: " + key);return delegate.get(key);}// 實現其他方法...
}
3. 代理模式
3.1 靜態代理與動態代理
代理模式為其他對象提供一種代理以控制對這個對象的訪問。
3.1.1.靜態代理
// 接口
interface UserService {void addUser(String username);
}// 真實對象
class UserServiceImpl implements UserService {@Overridepublic void addUser(String username) {System.out.println("添加用戶: " + username);}
}// 靜態代理
class UserServiceProxy implements UserService {private UserService userService;public UserServiceProxy(UserService userService) {this.userService = userService;}@Overridepublic void addUser(String username) {System.out.println("開始執行添加用戶操作");userService.addUser(username);System.out.println("添加用戶操作完成");}
}// 使用
public class StaticProxyDemo {public static void main(String[] args) {UserService userService = new UserServiceImpl();UserService proxy = new UserServiceProxy(userService);proxy.addUser("張三");}
}
3.1.2.JDK動態代理
// 動態代理處理器
class LoggingInvocationHandler implements InvocationHandler {private final Object target;public LoggingInvocationHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("Before method: " + method.getName());Object result = method.invoke(target, args);System.out.println("After method: " + method.getName());return result;}
}// 使用
public class JdkProxyDemo {public static void main(String[] args) {UserService userService = new UserServiceImpl();UserService proxy = (UserService) Proxy.newProxyInstance(UserService.class.getClassLoader(),new Class[]{UserService.class},new LoggingInvocationHandler(userService));proxy.addUser("李四");}
}
3.1.3.CGLIB動態代理
// 需要引入cglib依賴
class UserServiceInterceptor implements MethodInterceptor {@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("Before method: " + method.getName());Object result = proxy.invokeSuper(obj, args);System.out.println("After method: " + method.getName());return result;}
}// 使用
public class CglibProxyDemo {public static void main(String[] args) {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(UserServiceImpl.class);enhancer.setCallback(new UserServiceInterceptor());UserService proxy = (UserService) enhancer.create();proxy.addUser("王五");}
}
3.2 實際應用場景
3.2.1.Spring AOP實現原理
@Aspect
@Component
public class LoggingAspect {@Before("execution(* com.example.service.*.*(..))")public void logBefore(JoinPoint joinPoint) {System.out.println("Before method: " + joinPoint.getSignature().getName());}@AfterReturning(pointcut = "execution(* com.example.service.*.*(..))", returning = "result")public void logAfterReturning(JoinPoint joinPoint, Object result) {System.out.println("After method: " + joinPoint.getSignature().getName() + ", result: " + result);}
}// Spring AOP底層使用JDK動態代理或CGLIB實現
3.2.2.MyBatis的Mapper接口代理
// MyBatis的Mapper接口
public interface UserMapper {@Select("SELECT * FROM users WHERE id = #{id}")User getUserById(int id);
}// MyBatis會為這個接口創建動態代理
SqlSession session = sqlSessionFactory.openSession();
try {UserMapper mapper = session.getMapper(UserMapper.class);User user = mapper.getUserById(1);
} finally {session.close();
}
3.2.3.RPC框架中的遠程調用
// RPC客戶端代理
public class RpcProxy implements InvocationHandler {private String host;private int port;public RpcProxy(String host, int port) {this.host = host;this.port = port;}@SuppressWarnings("unchecked")public <T> T getProxy(Class<T> clazz) {return (T) Proxy.newProxyInstance(clazz.getClassLoader(),new Class<?>[]{clazz},this);}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 構造RPC請求RpcRequest request = new RpcRequest();request.setClassName(method.getDeclaringClass().getName());request.setMethodName(method.getName());request.setParameterTypes(method.getParameterTypes());request.setParameters(args);// 發送網絡請求return sendRequest(request);}private Object sendRequest(RpcRequest request) {// 實際網絡通信代碼...return null;}
}// 使用
public class RpcClient {public static void main(String[] args) {RpcProxy proxy = new RpcProxy("localhost", 8080);UserService userService = proxy.getProxy(UserService.class);userService.addUser("遠程用戶");}
}
4. 外觀模式
4.1 簡化復雜子系統
外觀模式提供了一個統一的接口,用來訪問子系統中的一群接口。
4.1.1.門面角色的設計
// 子系統1
class CPU {public void start() {System.out.println("CPU is starting...");}public void shutdown() {System.out.println("CPU is shutting down...");}
}// 子系統2
class Memory {public void load() {System.out.println("Memory is loading data...");}public void free() {System.out.println("Memory is freeing data...");}
}// 子系統3
class HardDrive {public void read() {System.out.println("HardDrive is reading data...");}public void write() {System.out.println("HardDrive is writing data...");}
}// 外觀類
class ComputerFacade {private CPU cpu;private Memory memory;private HardDrive hardDrive;public ComputerFacade() {this.cpu = new CPU();this.memory = new Memory();this.hardDrive = new HardDrive();}public void start() {System.out.println("Computer starting...");cpu.start();memory.load();hardDrive.read();System.out.println("Computer started successfully");}public void shutdown() {System.out.println("Computer shutting down...");cpu.shutdown();memory.free();hardDrive.write();System.out.println("Computer shutdown successfully");}
}// 使用
public class FacadeDemo {public static void main(String[] args) {ComputerFacade computer = new ComputerFacade();computer.start();System.out.println("----------------");computer.shutdown();}
}
4.1.2.與代理模式的區別
- 代理模式通常代表一個單一對象,而外觀模式代表一個子系統
- 代理模式控制對對象的訪問,外觀模式提供簡化的接口
- 代理模式通常一對一,外觀模式通常一對多
4.2 實際應用場景
4.2.1.SLF4J簡化日志API
// SLF4J作為外觀,底層可以適配多種日志實現
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class Slf4jFacadeExample {private static final Logger logger = LoggerFactory.getLogger(Slf4jFacadeExample.class);public static void main(String[] args) {logger.debug("Debug message");logger.info("Info message");logger.error("Error message");}
}
4.2.2.Spring的JdbcTemplate
@Repository
public class UserRepository {@Autowiredprivate JdbcTemplate jdbcTemplate;public User findById(int id) {// JdbcTemplate封裝了JDBC的復雜操作return jdbcTemplate.queryForObject("SELECT * FROM users WHERE id = ?",new Object[]{id},(rs, rowNum) -> {User user = new User();user.setId(rs.getInt("id"));user.setName(rs.getString("name"));return user;});}public void save(User user) {jdbcTemplate.update("INSERT INTO users (name) VALUES (?)",user.getName());}
}
4.2.3.微服務網關設計
// 簡化的API網關外觀
@RestController
@RequestMapping("/api")
public class ApiGateway {@Autowiredprivate UserServiceClient userService;@Autowiredprivate OrderServiceClient orderService;@Autowiredprivate ProductServiceClient productService;@GetMapping("/user/{userId}/details")public ResponseEntity<UserDetails> getUserDetails(@PathVariable int userId) {// 聚合多個微服務的調用User user = userService.getUser(userId);List<Order> orders = orderService.getUserOrders(userId);UserDetails details = new UserDetails();details.setUser(user);details.setOrders(orders);return ResponseEntity.ok(details);}@PostMapping("/order")public ResponseEntity<Order> createOrder(@RequestBody OrderRequest request) {// 驗證用戶User user = userService.getUser(request.getUserId());// 驗證產品Product product = productService.getProduct(request.getProductId());// 創建訂單Order order = orderService.createOrder(request.getUserId(), request.getProductId(), request.getQuantity());return ResponseEntity.ok(order);}
}
5. 組合模式
5.1 部分-整體層次結構
組合模式將對象組合成樹形結構以表示"部分-整體"的層次結構。
透明模式與安全模式
5.1.1.透明模式(所有方法都在Component中定義)
// 組件接口
interface FileSystemComponent {void display();void add(FileSystemComponent component);void remove(FileSystemComponent component);FileSystemComponent getChild(int index);
}// 葉子組件
class File implements FileSystemComponent {private String name;public File(String name) {this.name = name;}@Overridepublic void display() {System.out.println("File: " + name);}// 葉子節點不需要實現這些方法@Overridepublic void add(FileSystemComponent component) {throw new UnsupportedOperationException();}@Overridepublic void remove(FileSystemComponent component) {throw new UnsupportedOperationException();}@Overridepublic FileSystemComponent getChild(int index) {throw new UnsupportedOperationException();}
}// 復合組件
class Directory implements FileSystemComponent {private String name;private List<FileSystemComponent> children = new ArrayList<>();public Directory(String name) {this.name = name;}@Overridepublic void display() {System.out.println("Directory: " + name);for (FileSystemComponent component : children) {component.display();}}@Overridepublic void add(FileSystemComponent component) {children.add(component);}@Overridepublic void remove(FileSystemComponent component) {children.remove(component);}@Overridepublic FileSystemComponent getChild(int index) {return children.get(index);}
}
5.1.2.安全模式(只在Composite中定義管理子組件的方法)
// 安全模式的組件接口
interface FileSystemComponent {void display();
}// 安全模式的復合組件
class Directory implements FileSystemComponent {private String name;private List<FileSystemComponent> children = new ArrayList<>();public Directory(String name) {this.name = name;}@Overridepublic void display() {System.out.println("Directory: " + name);for (FileSystemComponent component : children) {component.display();}}// 這些方法不在接口中定義public void add(FileSystemComponent component) {children.add(component);}public void remove(FileSystemComponent component) {children.remove(component);}public FileSystemComponent getChild(int index) {return children.get(index);}
}// 使用示例
public class CompositeDemo {public static void main(String[] args) {// 透明模式使用FileSystemComponent root = new Directory("root");FileSystemComponent home = new Directory("home");FileSystemComponent documents = new Directory("documents");root.add(home);home.add(documents);documents.add(new File("resume.doc"));documents.add(new File("notes.txt"));root.display();}
}
5.1.3.樹形結構的表示
// 更復雜的樹形結構示例
class TreeNode {private String name;private List<TreeNode> children = new ArrayList<>();private TreeNode parent;public TreeNode(String name) {this.name = name;}public void addChild(TreeNode child) {child.setParent(this);this.children.add(child);}public void removeChild(TreeNode child) {child.setParent(null);this.children.remove(child);}public List<TreeNode> getChildren() {return Collections.unmodifiableList(children);}public TreeNode getParent() {return parent;}private void setParent(TreeNode parent) {this.parent = parent;}public void traverse() {System.out.println(name);for (TreeNode child : children) {child.traverse();}}
}// 使用
public class TreeDemo {public static void main(String[] args) {TreeNode root = new TreeNode("Root");TreeNode child1 = new TreeNode("Child1");TreeNode child2 = new TreeNode("Child2");root.addChild(child1);root.addChild(child2);TreeNode grandChild1 = new TreeNode("GrandChild1");TreeNode grandChild2 = new TreeNode("GrandChild2");child1.addChild(grandChild1);child1.addChild(grandChild2);root.traverse();}
}
5.2 實際應用場景
5.2.1.GUI組件系統
// GUI組件示例
interface GUIComponent {void render();void add(GUIComponent component);void remove(GUIComponent component);
}class Window implements GUIComponent {private List<GUIComponent> components = new ArrayList<>();private String title;public Window(String title) {this.title = title;}@Overridepublic void render() {System.out.println("Rendering Window: " + title);for (GUIComponent component : components) {component.render();}}@Overridepublic void add(GUIComponent component) {components.add(component);}@Overridepublic void remove(GUIComponent component) {components.remove(component);}
}class Panel implements GUIComponent {private List<GUIComponent> components = new ArrayList<>();private String name;public Panel(String name) {this.name = name;}@Overridepublic void render() {System.out.println("Rendering Panel: " + name);for (GUIComponent component : components) {component.render();}}@Overridepublic void add(GUIComponent component) {components.add(component);}@Overridepublic void remove(GUIComponent component) {components.remove(component);}
}class Button implements GUIComponent {private String label;public Button(String label) {this.label = label;}@Overridepublic void render() {System.out.println("Rendering Button: " + label);}@Overridepublic void add(GUIComponent component) {throw new UnsupportedOperationException();}@Overridepublic void remove(GUIComponent component) {throw new UnsupportedOperationException();}
}// 使用
public class GuiDemo {public static void main(String[] args) {Window window = new Window("Main Window");Panel mainPanel = new Panel("Main Panel");Panel sidePanel = new Panel("Side Panel");Button okButton = new Button("OK");Button cancelButton = new Button("Cancel");mainPanel.add(okButton);mainPanel.add(cancelButton);window.add(mainPanel);window.add(sidePanel);window.render();}
}
5.2.2.組織架構表示
// 組織架構示例
class Employee {private String name;private String position;private List<Employee> subordinates = new ArrayList<>();public Employee(String name, String position) {this.name = name;this.position = position;}public void addSubordinate(Employee employee) {subordinates.add(employee);}public void removeSubordinate(Employee employee) {subordinates.remove(employee);}public List<Employee> getSubordinates() {return subordinates;}public void printOrganization(String indent) {System.out.println(indent + position + ": " + name);for (Employee subordinate : subordinates) {subordinate.printOrganization(indent + " ");}}
}// 使用
public class OrganizationDemo {public static void main(String[] args) {Employee ceo = new Employee("John", "CEO");Employee headSales = new Employee("Robert", "Head Sales");Employee headMarketing = new Employee("Michelle", "Head Marketing");Employee salesExecutive1 = new Employee("Richard", "Sales Executive");Employee salesExecutive2 = new Employee("Bob", "Sales Executive");Employee clerk1 = new Employee("Laura", "Marketing Clerk");Employee clerk2 = new Employee("James", "Marketing Clerk");ceo.addSubordinate(headSales);ceo.addSubordinate(headMarketing);headSales.addSubordinate(salesExecutive1);headSales.addSubordinate(salesExecutive2);headMarketing.addSubordinate(clerk1);headMarketing.addSubordinate(clerk2);ceo.printOrganization("");}
}
5.2.3.XML文檔處理
// XML節點抽象
interface XmlNode {void writeXml(StringBuilder xml, int indent);
}class XmlElement implements XmlNode {private String tag;private List<XmlNode> children = new ArrayList<>();private Map<String, String> attributes = new HashMap<>();public XmlElement(String tag) {this.tag = tag;}public void addAttribute(String name, String value) {attributes.put(name, value);}public void addChild(XmlNode child) {children.add(child);}@Overridepublic void writeXml(StringBuilder xml, int indent) {indent(xml, indent);xml.append("<").append(tag);for (Map.Entry<String, String> entry : attributes.entrySet()) {xml.append(" ").append(entry.getKey()).append("=\"").append(entry.getValue()).append("\"");}if (children.isEmpty()) {xml.append("/>\n");} else {xml.append(">\n");for (XmlNode child : children) {child.writeXml(xml, indent + 2);}indent(xml, indent);xml.append("</").append(tag).append(">\n");}}private void indent(StringBuilder xml, int indent) {for (int i = 0; i < indent; i++) {xml.append(" ");}}
}class XmlText implements XmlNode {private String text;public XmlText(String text) {this.text = text;}@Overridepublic void writeXml(StringBuilder xml, int indent) {indent(xml, indent);xml.append(text).append("\n");}private void indent(StringBuilder xml, int indent) {for (int i = 0; i < indent; i++) {xml.append(" ");}}
}// 使用
public class XmlDemo {public static void main(String[] args) {XmlElement root = new XmlElement("bookstore");XmlElement book1 = new XmlElement("book");book1.addAttribute("category", "COOKING");XmlElement title1 = new XmlElement("title");title1.addChild(new XmlText("Everyday Italian"));XmlElement author1 = new XmlElement("author");author1.addChild(new XmlText("Giada De Laurentiis"));book1.addChild(title1);book1.addChild(author1);root.addChild(book1);StringBuilder xml = new StringBuilder();root.writeXml(xml, 0);System.out.println(xml.toString());}
}
6. 享元模式
6.1 對象共享與池化技術
享元模式通過共享技術有效地支持大量細粒度對象的復用。
6.1.1.內部狀態與外部狀態
// 享元接口
interface Shape {void draw(int x, int y, int width, int height);
}// 具體享元
class Circle implements Shape {private String color;public Circle(String color) {this.color = color;}@Overridepublic void draw(int x, int y, int width, int height) {System.out.println("Drawing Circle: Color=" + color + ", x=" + x + ", y=" + y + ", width=" + width + ", height=" + height);}
}// 享元工廠
class ShapeFactory {private static final Map<String, Shape> circleMap = new HashMap<>();public static Shape getCircle(String color) {Shape circle = circleMap.get(color);if (circle == null) {circle = new Circle(color);circleMap.put(color, circle);System.out.println("Creating new circle of color: " + color);}return circle;}public static int getCircleCount() {return circleMap.size();}
}// 使用
public class FlyweightDemo {private static final String[] COLORS = {"Red", "Green", "Blue", "White", "Black"};public static void main(String[] args) {for (int i = 0; i < 20; ++i) {String color = COLORS[(int)(Math.random() * COLORS.length)];Shape circle = ShapeFactory.getCircle(color);circle.draw((int)(Math.random() * 100),(int)(Math.random() * 100),(int)(Math.random() * 50 + 50),(int)(Math.random() * 50 + 50));}System.out.println("Total circles created: " + ShapeFactory.getCircleCount());}
}
6.1.2.線程池的實現原理
// 簡化的線程池實現
class ThreadPool {private BlockingQueue<Runnable> taskQueue;private List<WorkerThread> threads = new ArrayList<>();private boolean isStopped = false;public ThreadPool(int numberOfThreads) {taskQueue = new LinkedBlockingQueue<>();for (int i = 0; i < numberOfThreads; i++) {threads.add(new WorkerThread(taskQueue));}for (WorkerThread thread : threads) {thread.start();}}public synchronized void execute(Runnable task) throws Exception {if (isStopped) {throw new IllegalStateException("ThreadPool is stopped");}taskQueue.put(task);}public synchronized void stop() {isStopped = true;for (WorkerThread thread : threads) {thread.doStop();}}
}class WorkerThread extends Thread {private BlockingQueue<Runnable> taskQueue;private boolean isStopped = false;public WorkerThread(BlockingQueue<Runnable> queue) {taskQueue = queue;}public void run() {while (!isStopped()) {try {Runnable task = taskQueue.take();task.run();} catch (Exception e) {// 處理異常}}}public synchronized void doStop() {isStopped = true;this.interrupt(); // 中斷處于等待狀態的線程}public synchronized boolean isStopped() {return isStopped;}
}// 使用
public class ThreadPoolDemo {public static void main(String[] args) throws Exception {ThreadPool pool = new ThreadPool(3);for (int i = 0; i < 10; i++) {int taskNo = i;pool.execute(() -> {System.out.println("Task " + taskNo + " executed by " + Thread.currentThread().getName());});}Thread.sleep(2000);pool.stop();}
}
6.2 實際應用場景
6.2.1.字符串常量池
public class StringPoolDemo {public static void main(String[] args) {String s1 = "Hello";String s2 = "Hello";String s3 = new String("Hello").intern();System.out.println(s1 == s2); // trueSystem.out.println(s1 == s3); // trueString s4 = new String("Hello");System.out.println(s1 == s4); // falseSystem.out.println(s1.equals(s4)); // true}
}
6.2.2.數據庫連接池
// 使用HikariCP連接池
public class DatabasePoolExample {public static void main(String[] args) {HikariConfig config = new HikariConfig();config.setJdbcUrl("jdbc:mysql://localhost:3306/test");config.setUsername("root");config.setPassword("password");config.setMaximumPoolSize(10);config.setMinimumIdle(5);try (HikariDataSource dataSource = new HikariDataSource(config)) {// 從池中獲取連接try (Connection connection = dataSource.getConnection()) {Statement statement = connection.createStatement();ResultSet rs = statement.executeQuery("SELECT * FROM users");while (rs.next()) {System.out.println(rs.getString("name"));}}} catch (SQLException e) {e.printStackTrace();}}
}
6.2.3.Integer.valueOf()緩存
public class IntegerCacheDemo {public static void main(String[] args) {Integer i1 = 127; // 自動裝箱,使用緩存Integer i2 = 127;System.out.println(i1 == i2); // trueInteger i3 = 128; // 超出緩存范圍Integer i4 = 128;System.out.println(i3 == i4); // falseInteger i5 = Integer.valueOf(127); // 使用緩存Integer i6 = Integer.valueOf(127);System.out.println(i5 == i6); // trueInteger i7 = new Integer(
7.橋接模式:解耦抽象與實現的設計藝術
橋接模式是一種結構型設計模式,它通過將抽象部分與實現部分分離,使它們可以獨立地變化,從而解決多維度的擴展問題。這種模式特別適用于系統中存在多個獨立變化維度的場景,能夠有效避免類爆炸問題。
7.1 抽象與實現分離
7.1.1.多維度變化的解耦
橋接模式的核心思想是通過組合代替繼承來解耦抽象與實現。在傳統繼承方式中,如果有兩個變化維度(如形狀和顏色),每增加一種形狀和顏色的組合都會導致子類數量呈乘積增長。而橋接模式通過將其中一個維度(如顏色)抽取出來作為獨立的類層次結構,并通過組合方式與另一個維度(如形狀)建立關聯,從而將類數量從乘積關系變為加和關系。
橋接模式的UML類圖包含以下關鍵角色:
- Abstraction:抽象化角色,定義抽象接口,包含一個對實現化對象的引用
- RefinedAbstraction:擴展抽象化角色,實現父類業務方法并通過組合調用實現化角色
- Implementor:實現化角色接口,定義實現部分的接口
- ConcreteImplementor:具體實現化角色?
// 實現部分接口
interface Color {String fill();
}// 具體實現
class Red implements Color {public String fill() {return "紅色";}
}class Blue implements Color {public String fill() {return "藍色";}
}// 抽象部分
abstract class Shape {protected Color color;public Shape(Color color) {this.color = color;}public abstract String draw();
}// 擴展抽象
class Circle extends Shape {public Circle(Color color) {super(color);}public String draw() {return "圓形(" + color.fill() + ")";}
}class Square extends Shape {public Square(Color color) {super(color);}public String draw() {return "正方形(" + color.fill() + ")";}
}
7.1.2.與策略模式的區別
橋接模式和策略模式在結構上非常相似,都使用了組合來解耦,但它們的意圖和應用場景有本質區別:
目的不同:
- 橋接模式關注的是抽象與實現的分離,使兩者可以獨立變化,解決的是"多維度變化"問題
- 策略模式關注的是算法的封裝與替換,解決的是"多種算法選擇"問題?
抽象層級不同:
- 橋接模式中的抽象部分和實現部分通常是平行的兩個維度(如形狀和顏色)
- 策略模式中的策略通常是同一行為的多種實現(如不同的排序算法)
變化方向:
- 橋接模式處理的是正交維度的變化(形狀和顏色互不影響)
- 策略模式處理的是同一維度的不同算法實現?
// 策略模式示例 - 支付方式策略
interface PaymentStrategy {void pay(double amount);
}class CreditCardPayment implements PaymentStrategy {public void pay(double amount) {System.out.println("使用信用卡支付:" + amount);}
}class AlipayPayment implements PaymentStrategy {public void pay(double amount) {System.out.println("使用支付寶支付:" + amount);}
}// 上下文
class PaymentContext {private PaymentStrategy strategy;public void setStrategy(PaymentStrategy strategy) {this.strategy = strategy;}public void executePayment(double amount) {strategy.pay(amount);}
}
7.2 實際應用場景
7.2.1.JDBC驅動架構
JDBC(Java Database Connectivity)是橋接模式的經典應用。JDBC為所有關系型數據庫提供通用接口(抽象部分),而具體數據庫廠商提供實現(實現部分)。
JDBC架構中的橋接模式體現:
- 抽象部分:
Connection
、Statement
、ResultSet
等接口 - 實現部分:各數據庫廠商提供的驅動實現類(如MySQL的
com.mysql.jdbc.Driver
)
// JDBC使用示例展示橋接模式
public class JdbcExample {public static void main(String[] args) throws Exception {// 1. 加載驅動(實現部分)Class.forName("com.mysql.cj.jdbc.Driver");// 2. 獲取連接(抽象部分)Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "user", "password");// 3. 創建語句Statement stmt = conn.createStatement();// 4. 執行查詢ResultSet rs = stmt.executeQuery("SELECT * FROM users");// 5. 處理結果while(rs.next()) {System.out.println(rs.getString("username"));}// 6. 關閉連接conn.close();}
}
JDBC驅動注冊機制也體現了橋接模式的思想。當調用Class.forName()
加載驅動類時,驅動類通過靜態塊向DriverManager
注冊自己:
// MySQL驅動實現
public class Driver extends NonRegisteringDriver implements java.sql.Driver {static {try {DriverManager.registerDriver(new Driver());} catch (SQLException e) {throw new RuntimeException("Can't register driver!");}}
}
這種設計使得應用程序可以在不修改代碼的情況下,通過更換驅動來支持不同的數據庫,完美體現了抽象與實現分離的原則。
7.2.2.消息中間件API設計
消息中間件(如RabbitMQ、Kafka等)的API設計也廣泛應用了橋接模式。消息中間件通常需要支持多種協議(如AMQP、MQTT、STOMP)和多種消息模式(如點對點、發布/訂閱),這些變化維度可以通過橋接模式解耦。
消息中間件中的橋接模式結構:
- 抽象部分:消息發送/接收的核心接口
- 實現部分:不同協議的具體實現
// 消息發送抽象
interface MessageSender {void send(String topic, String message);
}// 消息接收抽象
interface MessageReceiver {void subscribe(String topic, MessageHandler handler);
}// 實現部分 - Kafka實現
class KafkaMessageSender implements MessageSender {private KafkaProducer producer;public KafkaMessageSender(KafkaProducer producer) {this.producer = producer;}public void send(String topic, String message) {producer.send(new ProducerRecord(topic, message));}
}class KafkaMessageReceiver implements MessageReceiver {private KafkaConsumer consumer;public KafkaMessageReceiver(KafkaConsumer consumer) {this.consumer = consumer;}public void subscribe(String topic, MessageHandler handler) {consumer.subscribe(Collections.singletonList(topic));while(true) {ConsumerRecords records = consumer.poll(Duration.ofMillis(100));for(ConsumerRecord record : records) {handler.handle(record.value());}}}
}// 客戶端代碼
public class MessageClient {private MessageSender sender;private MessageReceiver receiver;public MessageClient(MessageSender sender, MessageReceiver receiver) {this.sender = sender;this.receiver = receiver;}public void process() {receiver.subscribe("test", message -> {System.out.println("Received: " + message);sender.send("response", "Processed: " + message);});}
}
這種設計允許開發者獨立擴展消息協議和消息處理邏輯,例如可以輕松添加RabbitMQ實現而不影響現有代碼。
7.2.3.跨平臺圖形渲染
跨平臺圖形渲染引擎是橋接模式的另一個典型應用場景。這類系統通常需要處理兩個主要變化維度:渲染API(如OpenGL、Vulkan、Metal)和平臺抽象(如Windows、Linux、macOS)。
跨平臺渲染引擎的橋接模式實現:
// 渲染API接口(實現部分)
interface RenderAPI {void renderTriangle(float x1, float y1, float x2, float y2, float x3, float y3);void renderRectangle(float x, float y, float width, float height);
}// OpenGL實現
class OpenGLRender implements RenderAPI {public void renderTriangle(float x1, float y1, float x2, float y2, float x3, float y3) {System.out.println("OpenGL渲染三角形");// 實際OpenGL調用}public void renderRectangle(float x, float y, float width, float height) {System.out.println("OpenGL渲染矩形");// 實際OpenGL調用}
}// Vulkan實現
class VulkanRender implements RenderAPI {public void renderTriangle(float x1, float y1, float x2, float y2, float x3, float y3) {System.out.println("Vulkan渲染三角形");// 實際Vulkan調用}public void renderRectangle(float x, float y, float width, float height) {System.out.println("Vulkan渲染矩形");// 實際Vulkan調用}
}// 圖形抽象(抽象部分)
abstract class Shape {protected RenderAPI renderAPI;protected Shape(RenderAPI renderAPI) {this.renderAPI = renderAPI;}public abstract void draw();
}// 具體圖形
class Triangle extends Shape {private float x1, y1, x2, y2, x3, y3;public Triangle(RenderAPI renderAPI, float x1, float y1, float x2, float y2, float x3, float y3) {super(renderAPI);this.x1 = x1; this.y1 = y1;this.x2 = x2; this.y2 = y2;this.x3 = x3; this.y3 = y3;}public void draw() {renderAPI.renderTriangle(x1, y1, x2, y2, x3, y3);}
}class Rectangle extends Shape {private float x, y, width, height;public Rectangle(RenderAPI renderAPI, float x, float y, float width, float height) {super(renderAPI);this.x = x; this.y = y;this.width = width; this.height = height;}public void draw() {renderAPI.renderRectangle(x, y, width, height);}
}// 平臺抽象層
interface PlatformWindow {void createWindow();void swapBuffers();
}// Windows平臺實現
class WindowsWindow implements PlatformWindow {public void createWindow() {System.out.println("創建Windows窗口");}public void swapBuffers() {System.out.println("Windows交換緩沖區");}
}// Linux平臺實現
class LinuxWindow implements PlatformWindow {public void createWindow() {System.out.println("創建Linux窗口");}public void swapBuffers() {System.out.println("Linux交換緩沖區");}
}
這種架構設計使得圖形渲染引擎可以:
- 獨立擴展渲染API(如新增Metal支持)
- 獨立擴展平臺支持(如新增Android支持)
- 任意組合渲染API和平臺?
在實際的跨平臺渲染引擎中,橋接模式通常與其他模式(如抽象工廠)結合使用,以處理更復雜的場景,如不同平臺的資源管理、線程模型等。
7.3.橋接模式的最佳實踐
7.3.1.適用場景總結
根據實際開發經驗,橋接模式特別適用于以下場景:
-
多維度變化系統:當一個類存在兩個或更多獨立變化的維度,且這些維度都需要擴展時
示例:圖形編輯器中的形狀和顏色、支付系統中的支付方式和支付渠道 -
避免繼承爆炸:當使用繼承會導致類層次結構急劇膨脹時
示例:N種形狀×M種顏色,使用繼承需要N×M個子類,橋接模式只需N+M個類 -
運行時綁定實現:需要在運行時切換不同實現時
示例:動態切換數據庫連接、動態更換主題/皮膚 -
共享實現:多個對象需要共享一個實現,同時不希望客戶端感知到這種共享時
示例:多個View共享同一個渲染引擎
7.3.2.實現建議
合理識別變化維度:正確識別系統中獨立變化的維度是應用橋接模式的關鍵
分析需求變化點,找出真正獨立的變化軸?設計清晰的抽象接口:抽象部分應專注于高層邏輯,將具體工作委派給實現部分
抽象接口應保持穩定,避免頻繁變化?使用依賴注入:通過構造函數或setter注入實現部分,提高靈活性
示例:new Circle(new Red())
考慮默認實現:為常用場景提供默認實現,簡化客戶端代碼
示例:提供DefaultRenderAPI
作為基礎實現?與工廠模式結合:當實現部分的創建邏輯復雜時,可使用工廠管理實現對象的創建
示例:RenderAPIFactory.createAPI(type)
?
7.3.3.性能考量
橋接模式通過引入間接層(抽象與實現的分離)帶來了一些性能開銷,但在大多數場景下這種開銷可以忽略不計。性能敏感的場景可以考慮以下優化:
- 減少橋接調用:合并頻繁的橋接調用,減少跨層通信
- 緩存常用實現:對頻繁使用的實現對象進行緩存
- 對象池技術:對創建成本高的實現對象使用對象池?
7.4.反模式與常見誤區
盡管橋接模式功能強大,但在實際應用中也有一些需要注意的反模式和常見誤區:
過度設計:在變化維度固定的簡單系統中使用橋接模式會導致不必要的復雜性
建議:當變化維度不超過1個時,謹慎考慮是否真的需要橋接模式?錯誤識別維度:將非獨立的維度誤認為獨立維度,導致設計僵化
示例:將"顏色"和"透明度"作為獨立維度,而實際上它們都屬于"外觀"維度?抽象泄漏:實現細節通過抽象接口泄漏到客戶端
反例:抽象接口中包含實現特有的方法?混淆策略模式:將算法選擇問題誤用橋接模式解決
區分:策略模式針對同一行為的不同算法,橋接模式針對不同維度的變化?
7.5.現代Java中的橋接模式
隨著Java語言的發展,一些新特性可以使橋接模式的實現更加簡潔:
7.5.1.使用函數式接口
在Java 8+中,可以使用函數式接口簡化實現部分的定義:
// 使用函數式接口定義實現部分
interface Renderer {void render(Shape shape);
}// Lambda實現
Renderer openGLRenderer = shape -> {if(shape instanceof Circle) {// OpenGL渲染圓形} else if(shape instanceof Rectangle) {// OpenGL渲染矩形}
};Renderer vulkanRenderer = shape -> {if(shape instanceof Circle) {// Vulkan渲染圓形} else if(shape instanceof Rectangle) {// Vulkan渲染矩形}
};// 使用
Shape circle = new Circle(openGLRenderer, 10, 10, 5);
circle.draw();
7.5.2.結合模塊系統
Java 9引入的模塊系統(JPMS)可以幫助更好地組織橋接模式的代碼結構:
module graphics.core {exports com.example.graphics.abstractions;exports com.example.graphics.implementations;
}module graphics.opengl {requires graphics.core;provides com.example.graphics.implementations.RenderAPI with com.example.opengl.OpenGLRender;
}module graphics.vulkan {requires graphics.core;provides com.example.graphics.implementations.RenderAPI with com.example.vulkan.VulkanRender;
}
這種模塊化的組織方式使得抽象部分和實現部分可以完全獨立開發和部署。
7.6.總結
橋接模式是處理多維度變化系統的強大工具,它通過分離抽象和實現,提高了系統的擴展性和可維護性。在實際開發中,正確識別獨立變化維度是應用橋接模式的關鍵。JDBC驅動架構、消息中間件API和跨平臺渲染引擎等實際案例證明了橋接模式在復雜系統中的價值。
當面臨以下情況時,考慮使用橋接模式:
- 系統存在多個獨立變化的維度
- 繼承導致類層次結構過于復雜
- 需要在運行時切換實現
- 希望抽象部分和實現部分能夠獨立擴展?
記住,設計模式不是銀彈,橋接模式也不應被強制應用于所有場景。合理評估系統需求,在真正需要解耦抽象與實現的地方使用橋接模式,才能發揮其最大價值。
第四部分:行為型模式與應用
1. 模板方法模式
1.1 算法骨架與步驟
?1.1.1.鉤子方法的使用
?鉤子方法是在抽象類中聲明并實現的方法(通常為空實現或默認實現),子類可以選擇性地覆蓋它。
public abstract class Game {// 模板方法public final void play() {initialize();startPlay();endPlay();if (needCelebrate()) { // 鉤子方法celebrate();}}// 具體方法protected void initialize() {System.out.println("游戲初始化完成");}// 抽象方法protected abstract void startPlay();protected abstract void endPlay();// 鉤子方法protected boolean needCelebrate() {return false;}protected void celebrate() {// 默認實現為空}
}public class BasketballGame extends Game {@Overrideprotected void startPlay() {System.out.println("籃球比賽開始");}@Overrideprotected void endPlay() {System.out.println("籃球比賽結束");}@Overrideprotected boolean needCelebrate() {return true; // 覆蓋鉤子方法}@Overrideprotected void celebrate() {System.out.println("慶祝比賽勝利!");}
}
1.1.2.好萊塢原則
?"不要打電話給我們,我們會打給你"。高層組件決定何時調用底層組件,底層組件不直接調用高層組件。
?實際應用場景
- JUnit測試框架
public abstract class TestCase {// 模板方法public final void runBare() throws Throwable {setUp(); // 前置處理try {runTest(); // 執行測試} finally {tearDown(); // 后置處理}}protected void setUp() throws Exception {}protected void tearDown() throws Exception {}protected void runTest() throws Throwable {// 反射調用測試方法} }
- Spring的JdbcTemplate
public class JdbcTemplate {public <T> T execute(ConnectionCallback<T> action) throws DataAccessException {Connection con = DataSourceUtils.getConnection(getDataSource());try {// 設置連接屬性Connection conToUse = con;// 執行用戶定義的操作T result = action.doInConnection(conToUse);// 處理警告handleWarnings(con);return result;} catch (SQLException ex) {// 異常處理throw translateException("ConnectionCallback", sql, ex);} finally {// 釋放連接DataSourceUtils.releaseConnection(con, getDataSource());}} }
- Servlet的doGet/doPost
public abstract class HttpServlet extends GenericServlet {protected void service(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {String method = req.getMethod();if (method.equals("GET")) {doGet(req, resp);} else if (method.equals("POST")) {doPost(req, resp);}// 其他HTTP方法...}protected abstract void doGet(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException;protected abstract void doPost(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException; }
2. 策略模式
2.1 算法族的封裝
策略模式定義了一系列算法,將每個算法封裝起來,并使它們可以互相替換。
-
與命令模式的區別:
- 策略模式:關注算法替換,同一行為的不同實現
- 命令模式:將請求封裝為對象,支持撤銷、排隊等操
2.2 實際應用場景
- 支付方式選擇
// 策略接口 public interface PaymentStrategy {void pay(double amount); }// 具體策略 public class CreditCardPayment implements PaymentStrategy {private String cardNumber;public CreditCardPayment(String cardNumber) {this.cardNumber = cardNumber;}@Overridepublic void pay(double amount) {System.out.println("使用信用卡" + cardNumber + "支付" + amount + "元");} }public class AlipayPayment implements PaymentStrategy {private String account;public AlipayPayment(String account) {this.account = account;}@Overridepublic void pay(double amount) {System.out.println("使用支付寶" + account + "支付" + amount + "元");} }// 環境類 public class PaymentContext {private PaymentStrategy strategy;public void setStrategy(PaymentStrategy strategy) {this.strategy = strategy;}public void executePayment(double amount) {strategy.pay(amount);} }// 使用 PaymentContext context = new PaymentContext(); context.setStrategy(new CreditCardPayment("1234-5678-9012-3456")); context.executePayment(100.00);
- 排序算法切換
public interface SortStrategy {void sort(int[] array); }public class BubbleSort implements SortStrategy {@Overridepublic void sort(int[] array) {// 冒泡排序實現} }public class QuickSort implements SortStrategy {@Overridepublic void sort(int[] array) {// 快速排序實現} }public class Sorter {private SortStrategy strategy;public void setStrategy(SortStrategy strategy) {this.strategy = strategy;}public void sortArray(int[] array) {strategy.sort(array);} }
- Spring的ResourceLoader
public interface ResourceLoader {Resource getResource(String location); }public class DefaultResourceLoader implements ResourceLoader {@Overridepublic Resource getResource(String location) {// 根據location前綴選擇不同的Resource實現if (location.startsWith("classpath:")) {return new ClassPathResource(location.substring("classpath:".length()));} else if (location.startsWith("http:")) {return new UrlResource(location);}// 其他資源類型...} }
3.?觀察者模式
3.1 發布-訂閱機制
觀察者模式定義了對象之間的一對多依賴,當一個對象狀態改變時,所有依賴者都會收到通知。
?推模型與拉模型
- 推模型:主題將詳細數據推送給觀察者
- 拉模型:主題發送最小通知,觀察者主動拉取所需數據?
?線程安全問題
- 使用CopyOnWriteArrayList存儲觀察者
- 同步通知方法
- 使用事件隊列
3.2 實際應用場景
- Swing事件處理
button.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {// 處理按鈕點擊事件} });
- Spring的事件機制
// 自定義事件 public class OrderCreatedEvent extends ApplicationEvent {private Order order;public OrderCreatedEvent(Object source, Order order) {super(source);this.order = order;}public Order getOrder() {return order;} }// 事件發布者 @Service public class OrderService {@Autowiredprivate ApplicationEventPublisher eventPublisher;public void createOrder(Order order) {// 創建訂單邏輯...eventPublisher.publishEvent(new OrderCreatedEvent(this, order));} }// 事件監聽者 @Component public class OrderEventListener {@EventListenerpublic void handleOrderCreated(OrderCreatedEvent event) {// 處理訂單創建事件} }
- 消息隊列消費者
// RabbitMQ消費者示例 @RabbitListener(queues = "order.queue") public void receiveOrderMessage(OrderMessage message) {// 處理訂單消息 }
4. 責任鏈模式
4.1 請求處理鏈
責任鏈模式將請求的發送者和接收者解耦,使多個對象都有機會處理請求。
純與不純的責任鏈?
- 純責任鏈:請求必須被某個處理者處理
- 不純責任鏈:請求可以被部分處理或全部處理
?中斷鏈的條件
- 請求已被處理
- 請求無法被后續處理者處理
- 達到最大處理次數?
4.2 實際應用場景
- Servlet Filter
public class LogFilter implements Filter {@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {// 前置處理System.out.println("請求開始: " + request.getRemoteAddr());// 傳遞給下一個過濾器chain.doFilter(request, response);// 后置處理System.out.println("請求結束");} }
- Spring Security過濾器鏈
public class SecurityFilterChain {private List<Filter> filters;public void doFilter(ServletRequest request, ServletResponse response) {new VirtualFilterChain(filters).doFilter(request, response);}private static class VirtualFilterChain implements FilterChain {private final Iterator<Filter> iterator;public VirtualFilterChain(List<Filter> filters) {this.iterator = filters.iterator();}@Overridepublic void doFilter(ServletRequest request, ServletResponse response) {if (iterator.hasNext()) {Filter nextFilter = iterator.next();nextFilter.doFilter(request, response, this);}}} }
- 審批流程系統
public abstract class Approver {protected Approver successor;public void setSuccessor(Approver successor) {this.successor = successor;}public abstract void processRequest(PurchaseRequest request); }public class Manager extends Approver {@Overridepublic void processRequest(PurchaseRequest request) {if (request.getAmount() < 10000) {System.out.println("經理審批通過");} else if (successor != null) {successor.processRequest(request);}} }public class Director extends Approver {@Overridepublic void processRequest(PurchaseRequest request) {if (request.getAmount() < 50000) {System.out.println("總監審批通過");} else if (successor != null) {successor.processRequest(request);}} }
5. 狀態模式
5.1 狀態驅動的行為
狀態模式允許對象在內部狀態改變時改變其行為。
?狀態轉換邏輯
- 由Context類負責
- 由State類負責
- 由客戶端負責
?與策略模式的區別
- 狀態模式:狀態自動轉換,行為由狀態決定
- 策略模式:策略由客戶端選擇,行為由策略決定
5.2 實際應用場景
- 訂單狀態管理
public interface OrderState {void confirm(OrderContext context);void cancel(OrderContext context);void ship(OrderContext context); }public class NewOrderState implements OrderState {@Overridepublic void confirm(OrderContext context) {System.out.println("訂單已確認");context.setState(new ConfirmedState());}@Overridepublic void cancel(OrderContext context) {System.out.println("訂單已取消");context.setState(new CancelledState());}@Overridepublic void ship(OrderContext context) {System.out.println("新訂單不能直接發貨");} }public class OrderContext {private OrderState state;public OrderContext() {this.state = new NewOrderState();}public void setState(OrderState state) {this.state = state;}public void confirm() {state.confirm(this);}public void cancel() {state.cancel(this);}public void ship() {state.ship(this);} }
- 游戲角色狀態
public interface CharacterState {void walk();void run();void attack(); }public class NormalState implements CharacterState {@Overridepublic void walk() {System.out.println("正常行走");}@Overridepublic void run() {System.out.println("快速奔跑");}@Overridepublic void attack() {System.out.println("普通攻擊");} }public class PoisonedState implements CharacterState {@Overridepublic void walk() {System.out.println("中毒狀態,行走緩慢");}@Overridepublic void run() {System.out.println("中毒狀態,無法奔跑");}@Overridepublic void attack() {System.out.println("中毒狀態,攻擊力減半");} }
- TCP連接狀態
public interface TCPState {void open(TCPConnection connection);void close(TCPConnection connection);void acknowledge(TCPConnection connection); }public class TCPEstablished implements TCPState {@Overridepublic void open(TCPConnection connection) {System.out.println("連接已建立,無需再次打開");}@Overridepublic void close(TCPConnection connection) {System.out.println("關閉連接");connection.setState(new TCPClosed());}@Overridepublic void acknowledge(TCPConnection connection) {System.out.println("發送ACK確認");} }
6. 命令模式
6.1 請求封裝為對象
- 命令隊列與日志
public class CommandQueue {private Queue<Command> queue = new LinkedList<>();public void addCommand(Command command) {queue.add(command);}public void executeAll() {while (!queue.isEmpty()) {Command command = queue.poll();command.execute();}} }public class CommandLogger {private List<Command> history = new ArrayList<>();public void logAndExecute(Command command) {history.add(command);command.execute();}public void replay() {for (Command command : history) {command.execute();}} }
- 撤銷與重做實現
public interface Command {void execute();void undo(); }public class InsertTextCommand implements Command {private String text;private int position;private TextEditor editor;public InsertTextCommand(TextEditor editor, String text, int position) {this.editor = editor;this.text = text;this.position = position;}@Overridepublic void execute() {editor.insert(text, position);}@Overridepublic void undo() {editor.delete(position, text.length());} }public class TextEditor {private StringBuilder text = new StringBuilder();private Deque<Command> undoStack = new ArrayDeque<>();private Deque<Command> redoStack = new ArrayDeque<>();public void executeCommand(Command command) {command.execute();undoStack.push(command);redoStack.clear();}public void undo() {if (!undoStack.isEmpty()) {Command command = undoStack.pop();command.undo();redoStack.push(command);}}public void redo() {if (!redoStack.isEmpty()) {Command command = redoStack.pop();command.execute();undoStack.push(command);}} }
6.2 實際應用場景
- GUI菜單系統
public interface MenuItemCommand {void execute(); }public class SaveCommand implements MenuItemCommand {private Document document;public SaveCommand(Document document) {this.document = document;}@Overridepublic void execute() {document.save();} }public class MenuItem {private String label;private MenuItemCommand command;public MenuItem(String label, MenuItemCommand command) {this.label = label;this.command = command;}public void onClick() {command.execute();} }
- 事務管理
public interface TransactionCommand {void execute();void rollback(); }public class MoneyTransferCommand implements TransactionCommand {private Account from;private Account to;private BigDecimal amount;public MoneyTransferCommand(Account from, Account to, BigDecimal amount) {this.from = from;this.to = to;this.amount = amount;}@Overridepublic void execute() {from.debit(amount);to.credit(amount);}@Overridepublic void rollback() {to.debit(amount);from.credit(amount);} }public class TransactionManager {private List<TransactionCommand> commands = new ArrayList<>();public void addCommand(TransactionCommand command) {commands.add(command);}public void commit() {try {for (TransactionCommand command : commands) {command.execute();}} catch (Exception e) {rollback();throw e;}}public void rollback() {for (int i = commands.size() - 1; i >= 0; i--) {commands.get(i).rollback();}} }
- 異步任務隊列
public interface AsyncCommand {void execute(); }public class EmailNotificationCommand implements AsyncCommand {private String email;private String message;public EmailNotificationCommand(String email, String message) {this.email = email;this.message = message;}@Overridepublic void execute() {// 發送郵件邏輯System.out.println("發送郵件到 " + email + ": " + message);} }public class TaskQueue {private ExecutorService executor = Executors.newFixedThreadPool(4);private Queue<AsyncCommand> queue = new ConcurrentLinkedQueue<>();public void addTask(AsyncCommand command) {queue.add(command);}public void startProcessing() {while (!queue.isEmpty()) {AsyncCommand command = queue.poll();executor.submit(() -> command.execute());}} }
7. 訪問者模式
7.1 雙重分派機制
訪問者模式通過雙重分派實現數據結構和操作的分離。
- 對擴展開放:新增操作只需添加新的訪問者
- 對修改關閉:元素類不需要修改
7.2 實際應用場景
- 編譯器語法樹分析
public interface ASTNode {void accept(ASTVisitor visitor); }public class VariableNode implements ASTNode {private String name;@Overridepublic void accept(ASTVisitor visitor) {visitor.visit(this);} }public interface ASTVisitor {void visit(VariableNode node);void visit(AssignmentNode node);void visit(IfStatementNode node); }public class TypeCheckingVisitor implements ASTVisitor {@Overridepublic void visit(VariableNode node) {// 類型檢查邏輯}// 其他visit方法實現... }
- 文件系統遍歷
public interface FileSystemVisitor {void visit(File file);void visit(Directory directory); }public class FileSizeCalculator implements FileSystemVisitor {private long totalSize = 0;@Overridepublic void visit(File file) {totalSize += file.getSize();}@Overridepublic void visit(Directory directory) {// 目錄本身不占空間}public long getTotalSize() {return totalSize;} }
- ASM字節碼操作
public class MyClassVisitor extends ClassVisitor {public MyClassVisitor(int api, ClassVisitor cv) {super(api, cv);}@Overridepublic MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {// 處理方法return new MyMethodVisitor(api, super.visitMethod(access, name, desc, signature, exceptions));} }public class MyMethodVisitor extends MethodVisitor {public MyMethodVisitor(int api, MethodVisitor mv) {super(api, mv);}@Overridepublic void visitCode() {// 插入字節碼super.visitCode();} }
第五部分:設計模式綜合應用
1. 設計模式在框架中的應用
1.1 Spring框架中設計模式
?1.1.1.工廠模式:BeanFactory
Spring框架的核心IoC容器本質上是一個超級工廠,通過工廠模式管理應用對象的生命周期和配置。
核心實現:
// 工廠接口
public interface BeanFactory {Object getBean(String name) throws BeansException;<T> T getBean(String name, Class<T> requiredType) throws BeansException;// 其他工廠方法...
}// 具體實現 - DefaultListableBeanFactory
public class DefaultListableBeanFactory implements ConfigurableListableBeanFactory {private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);public Object getBean(String name) throws BeansException {return doGetBean(name, null, null, false);}protected <T> T doGetBean(...) {// 復雜的對象創建邏輯// 1. 檢查單例緩存// 2. 處理原型/其他作用域// 3. 依賴注入// 4. 初始化回調}
}
生產應用場景:
- 配置集中管理:所有Bean的創建都通過BeanFactory統一管理
- 依賴解耦:對象不再直接new,而是從工廠獲取
- 靈活擴展:可通過FactoryBean接口自定義復雜對象的創建邏輯
?最佳實踐:
// 自定義FactoryBean示例
public class MyServiceFactoryBean implements FactoryBean<MyService> {@Overridepublic MyService getObject() throws Exception {// 復雜對象的創建邏輯MyService service = new MyServiceImpl();service.setConfig(...);service.init();return service;}@Overridepublic Class<?> getObjectType() {return MyService.class;}
}
?1.1.2.代理模式:AOP實現
Spring AOP基于代理模式實現,為業務組件提供橫切關注點的能力。
動態代理實現原理:
// Spring AOP核心代理創建邏輯
public class DefaultAopProxyFactory implements AopProxyFactory {public AopProxy createAopProxy(AdvisedSupport config) {if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {Class<?> targetClass = config.getTargetClass();if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {return new JdkDynamicAopProxy(config); // JDK動態代理}return new ObjenesisCglibAopProxy(config); // CGLIB代理}return new JdkDynamicAopProxy(config);}
}
生產級AOP配置:
@Aspect
@Component
public class ServiceMonitorAspect {// 監控服務執行時間@Around("execution(* com..service.*.*(..))")public Object monitorServicePerformance(ProceedingJoinPoint pjp) throws Throwable {long start = System.currentTimeMillis();try {return pjp.proceed();} finally {long elapsed = System.currentTimeMillis() - start;if (elapsed > 1000) {log.warn("Slow service execution: {} took {}ms", pjp.getSignature(), elapsed);}}}// 事務重試邏輯@Around("@annotation(retryable)")public Object retryOperation(ProceedingJoinPoint pjp, Retryable retryable) throws Throwable {int attempts = 0;do {try {return pjp.proceed();} catch (RetryableException e) {if (++attempts >= retryable.maxAttempts()) {throw e;}Thread.sleep(retryable.backoff());}} while (true);}
}
1.1.3.模板方法:JdbcTemplate
Spring的JdbcTemplate是模板方法模式的經典實現,封裝了JDBC操作的固定流程。
核心模板結構:
public class JdbcTemplate extends JdbcAccessor implements JdbcOperations {public <T> T execute(ConnectionCallback<T> action) {Connection con = DataSourceUtils.getConnection(getDataSource());try {// 前置處理:設置連接屬性等Connection conToUse = con;// 執行用戶定義的操作T result = action.doInConnection(conToUse);// 后置處理:處理警告等handleWarnings(con);return result;} catch (SQLException ex) {// 異常轉換throw translateException("ConnectionCallback", sql, ex);} finally {// 資源釋放DataSourceUtils.releaseConnection(con, getDataSource());}}public <T> T query(String sql, RowMapper<T> rowMapper) {return execute(sql, new RowMapperResultSetExtractor<>(rowMapper));}
}
生產應用示例:
@Repository
public class UserRepository {private final JdbcTemplate jdbcTemplate;public UserRepository(DataSource dataSource) {this.jdbcTemplate = new JdbcTemplate(dataSource);}public List<User> findActiveUsers() {return jdbcTemplate.query("SELECT * FROM users WHERE status = ?", (rs, rowNum) -> new User(rs.getLong("id"),rs.getString("username"),rs.getString("email")),"ACTIVE");}@Transactionalpublic void batchInsert(List<User> users) {jdbcTemplate.batchUpdate("INSERT INTO users (username, email) VALUES (?, ?)",new BatchPreparedStatementSetter() {public void setValues(PreparedStatement ps, int i) {User user = users.get(i);ps.setString(1, user.getUsername());ps.setString(2, user.getEmail());}public int getBatchSize() {return users.size();}});}
}
1.2 JDK中的設計模式
?1.2.1.迭代器模式:Collection迭代
JDK集合框架通過迭代器模式提供統一的遍歷接口。
核心實現:
// 迭代器接口
public interface Iterator<E> {boolean hasNext();E next();default void remove() { ... }default void forEachRemaining(Consumer<? super E> action) { ... }
}// 集合接口
public interface Iterable<T> {Iterator<T> iterator();default void forEach(Consumer<? super T> action) { ... }default Spliterator<T> spliterator() { ... }
}// ArrayList中的具體實現
private class Itr implements Iterator<E> {int cursor; // 下一個元素的索引int lastRet = -1; // 上一個返回元素的索引int expectedModCount = modCount;public boolean hasNext() {return cursor != size;}public E next() {checkForComodification();int i = cursor;if (i >= size)throw new NoSuchElementException();Object[] elementData = ArrayList.this.elementData;if (i >= elementData.length)throw new ConcurrentModificationException();cursor = i + 1;return (E) elementData[lastRet = i];}final void checkForComodification() {if (modCount != expectedModCount)throw new ConcurrentModificationException();}
}
生產應用:
// 安全刪除元素的正確方式
public void removeNegativeNumbers(List<Integer> numbers) {Iterator<Integer> iterator = numbers.iterator();while (iterator.hasNext()) {if (iterator.next() < 0) {iterator.remove(); // 安全刪除當前元素}}
}// 并行迭代處理
public void processInParallel(Collection<Data> dataCollection) {Spliterator<Data> split1 = dataCollection.spliterator();Spliterator<Data> split2 = split1.trySplit();new Thread(() -> split1.forEachRemaining(this::process)).start();new Thread(() -> split2.forEachRemaining(this::process)).start();
}
1.2.2.裝飾器模式:IO流
Java I/O流體系是裝飾器模式的經典應用,通過層層包裝增強流的功能。
生產級IO操作:
// 帶緩沖的GZIP文件讀取
public byte[] readGzippedFile(Path filePath) throws IOException {try (InputStream in = new BufferedInputStream(new GZIPInputStream(new FileInputStream(filePath.toFile())))) {ByteArrayOutputStream out = new ByteArrayOutputStream();byte[] buffer = new byte[8192];int bytesRead;while ((bytesRead = in.read(buffer)) != -1) {out.write(buffer, 0, bytesRead);}return out.toByteArray();}
}// 帶校驗的加密輸出
public void writeEncryptedData(Path dest, byte[] data, SecretKey key) throws IOException {try (OutputStream out = new DigestOutputStream(new CipherOutputStream(new BufferedOutputStream(new FileOutputStream(dest.toFile())),createCipher(key, Cipher.ENCRYPT_MODE)),MessageDigest.getInstance("SHA-256"))) {out.write(data);byte[] digest = ((DigestOutputStream) out).getMessageDigest().digest();// 存儲摘要用于驗證Files.write(dest.resolveSibling(dest.getFileName() + ".sha256"), digest);}
}
1.2.3.觀察者模式:Event機制
JDK內置的觀察者模式實現提供了基本的事件通知機制。
核心組件:
// 被觀察者
public class Observable {private boolean changed = false;private Vector<Observer> obs = new Vector<>();public synchronized void addObserver(Observer o) {if (o == null) throw new NullPointerException();if (!obs.contains(o)) obs.addElement(o);}public void notifyObservers(Object arg) {Object[] arrLocal;synchronized (this) {if (!changed) return;arrLocal = obs.toArray();clearChanged();}for (int i = arrLocal.length-1; i>=0; i--)((Observer)arrLocal[i]).update(this, arg);}
}// 觀察者接口
public interface Observer {void update(Observable o, Object arg);
}
?生產級事件系統實現:
// 自定義事件總線
public class EventBus {private final Map<Class<?>, List<Consumer<Object>>> handlers = new ConcurrentHashMap<>();public <T> void subscribe(Class<T> eventType, Consumer<T> handler) {handlers.computeIfAbsent(eventType, k -> new CopyOnWriteArrayList<>()).add((Consumer<Object>) handler::accept);}public <T> void publish(T event) {List<Consumer<Object>> eventHandlers = handlers.get(event.getClass());if (eventHandlers != null) {eventHandlers.forEach(handler -> {try {handler.accept(event);} catch (Exception e) {System.err.println("Error handling event: " + e.getMessage());}});}}
}// 使用示例
public class OrderService {private final EventBus eventBus;public OrderService(EventBus eventBus) {this.eventBus = eventBus;eventBus.subscribe(OrderCreatedEvent.class, this::handleOrderCreated);}public void createOrder(Order order) {// 創建訂單邏輯...eventBus.publish(new OrderCreatedEvent(order));}private void handleOrderCreated(OrderCreatedEvent event) {// 發送通知、更新報表等}
}
1.3.設計模式最佳實踐總結
模式組合應用:
- Spring中經常組合使用工廠模式+單例模式管理Bean
- AOP中組合代理模式+責任鏈模式實現攔截器鏈
- JdbcTemplate組合模板方法+回調模式
性能考量:
- 代理模式選擇:JDK動態代理 vs CGLIB
- 單例模式:注意線程安全和性能影響
- 觀察者模式:避免過長的通知鏈影響性能
擴展性設計:
- 通過工廠模式支持新類型的創建
- 使用裝飾器模式透明增強功能
- 模板方法模式允許定制特定步驟
生產陷阱規避:
- 代理模式導致的this調用問題
- 觀察者模式的內存泄漏風險
- 裝飾器模式的過度包裝問題
這些設計模式在框架中的實現經過了長期的生產驗證,理解其原理和實現方式有助于我們更好地使用框架,并在自己的代碼中合理應用這些模式。
?MVC架構中的設計模式
- 組合模式:View層次
- 策略模式:Controller分發
- 觀察者模式:Model通知
微服務架構中的設計模式
- 外觀模式:API網關
- 代理模式:服務調用
- 單例模式:配置中心
2. 設計模式最佳實踐
3.1 模式組合應用
3.1.1.工廠+策略模式實現支付系統
理論描述: 工廠模式和策略模式的組合是解決多支付方式場景的經典方案。策略模式定義了一系列算法(支付方式),并將每個算法封裝起來,使它們可以互相替換;工廠模式負責創建這些策略對象,將對象的創建與使用分離。
這種組合的優勢在于:
- 解耦:支付策略的創建與業務邏輯分離
- 擴展性:新增支付方式只需添加新策略類,無需修改現有代碼
- 靈活性:運行時動態切換支付方式
代碼實現:
// 支付策略接口
public interface PaymentStrategy {void pay(double amount);String getType();
}// 具體支付策略實現
@Component
public class AlipayStrategy implements PaymentStrategy {@Overridepublic void pay(double amount) {System.out.println("支付寶支付: " + amount + "元");// 實際調用支付寶SDK}@Overridepublic String getType() {return "ALIPAY";}
}@Component
public class WechatPayStrategy implements PaymentStrategy {@Overridepublic void pay(double amount) {System.out.println("微信支付: " + amount + "元");// 實際調用微信支付SDK}@Overridepublic String getType() {return "WECHAT_PAY";}
}// 支付策略工廠
@Component
public class PaymentStrategyFactory {private final Map<String, PaymentStrategy> strategyMap = new ConcurrentHashMap<>();@Autowiredpublic PaymentStrategyFactory(List<PaymentStrategy> strategies) {strategies.forEach(strategy -> strategyMap.put(strategy.getType(), strategy));}public PaymentStrategy getStrategy(String paymentType) {PaymentStrategy strategy = strategyMap.get(paymentType);if (strategy == null) {throw new IllegalArgumentException("不支持的支付方式: " + paymentType);}return strategy;}
}// 支付服務
@Service
public class PaymentService {@Autowiredprivate PaymentStrategyFactory strategyFactory;public void processPayment(String paymentType, double amount) {PaymentStrategy strategy = strategyFactory.getStrategy(paymentType);strategy.pay(amount);}
}// 使用示例
@RestController
@RequestMapping("/payments")
public class PaymentController {@Autowiredprivate PaymentService paymentService;@PostMappingpublic ResponseEntity<String> pay(@RequestParam String type, @RequestParam double amount) {paymentService.processPayment(type, amount);return ResponseEntity.ok("支付處理中");}
}
實際應用場景:
- 電商平臺的多支付渠道接入?
- 金融系統的多種計費方式
- 會員系統的多種積分計算策略
3.1.2.觀察者+命令模式實現事件系統
理論描述: 觀察者模式用于實現發布-訂閱機制,命令模式將請求封裝為對象。兩者結合可以構建靈活的事件系統,其中:
- 觀察者模式負責事件的通知和響應
- 命令模式將事件處理邏輯封裝為可執行對象?
這種組合的優勢:
- 解耦:事件發布者與處理者完全解耦
- 靈活性:可以動態添加/移除事件處理器
- 可擴展:新增事件類型不影響現有代碼
代碼實現:
// 事件接口
public interface Event {String getType();
}// 命令接口
public interface EventHandler {void handle(Event event);boolean canHandle(Event event);
}// 具體事件
public class OrderCreatedEvent implements Event {private final Order order;public OrderCreatedEvent(Order order) {this.order = order;}@Overridepublic String getType() {return "ORDER_CREATED";}public Order getOrder() {return order;}
}// 具體事件處理器
@Component
public class SendEmailHandler implements EventHandler {@Overridepublic void handle(Event event) {OrderCreatedEvent orderEvent = (OrderCreatedEvent) event;System.out.println("發送訂單確認郵件給: " + orderEvent.getOrder().getCustomerEmail());}@Overridepublic boolean canHandle(Event event) {return event.getType().equals("ORDER_CREATED");}
}@Component
public class InventoryUpdateHandler implements EventHandler {@Overridepublic void handle(Event event) {OrderCreatedEvent orderEvent = (OrderCreatedEvent) event;System.out.println("更新庫存,減少商品: " + orderEvent.getOrder().getItems());}@Overridepublic boolean canHandle(Event event) {return event.getType().equals("ORDER_CREATED");}
}// 事件總線(觀察者模式實現)
@Service
public class EventBus {private final List<EventHandler> handlers = new CopyOnWriteArrayList<>();public void registerHandler(EventHandler handler) {handlers.add(handler);}public void unregisterHandler(EventHandler handler) {handlers.remove(handler);}public void publish(Event event) {handlers.stream().filter(handler -> handler.canHandle(event)).forEach(handler -> handler.handle(event));}
}// 使用示例
@RestController
@RequestMapping("/orders")
public class OrderController {@Autowiredprivate EventBus eventBus;@PostMappingpublic ResponseEntity<Order> createOrder(@RequestBody Order order) {// 保存訂單到數據庫...// 發布訂單創建事件eventBus.publish(new OrderCreatedEvent(order));return ResponseEntity.ok(order);}
}
實際應用場景:
- 訂單狀態變更通知系統
- 用戶行為追蹤和分析系統
- 微服務架構中的事件驅動通信
3.1.3.裝飾器+責任鏈模式實現過濾器鏈
理論描述: 裝飾器模式動態地給對象添加額外職責,責任鏈模式將請求的發送者和接收者解耦。兩者結合可以實現靈活的過濾器鏈:
- 裝飾器模式為每個過濾器添加統一接口
- 責任鏈模式組織過濾器的執行順序?
這種組合的優勢:
- 靈活性:可以動態添加/移除過濾器
- 可擴展:新增過濾器不影響現有邏輯
- 解耦:過濾器之間相互獨立
代碼實現:
// 過濾器接口
public interface Filter {void doFilter(Request request, Response response, FilterChain chain);
}// 過濾器鏈
public class FilterChain {private final List<Filter> filters = new ArrayList<>();private int index = 0;public FilterChain addFilter(Filter filter) {filters.add(filter);return this;}public void doFilter(Request request, Response response) {if (index < filters.size()) {Filter filter = filters.get(index++);filter.doFilter(request, response, this);}}
}// 具體過濾器實現
public class AuthenticationFilter implements Filter {@Overridepublic void doFilter(Request request, Response response, FilterChain chain) {System.out.println("執行認證檢查...");if (!request.getHeader("Authorization").equals("valid-token")) {response.setStatus(401);return;}chain.doFilter(request, response);}
}public class LoggingFilter implements Filter {@Overridepublic void doFilter(Request request, Response response, FilterChain chain) {System.out.println("請求日志: " + request.getPath());long start = System.currentTimeMillis();chain.doFilter(request, response);long duration = System.currentTimeMillis() - start;System.out.println("請求處理時間: " + duration + "ms");}
}// 裝飾器模式增強過濾器
public abstract class FilterDecorator implements Filter {protected Filter decoratedFilter;public FilterDecorator(Filter decoratedFilter) {this.decoratedFilter = decoratedFilter;}@Overridepublic void doFilter(Request request, Response response, FilterChain chain) {decoratedFilter.doFilter(request, response, chain);}
}public class MetricsFilterDecorator extends FilterDecorator {public MetricsFilterDecorator(Filter decoratedFilter) {super(decoratedFilter);}@Overridepublic void doFilter(Request request, Response response, FilterChain chain) {System.out.println("開始收集指標...");super.doFilter(request, response, chain);System.out.println("結束收集指標...");}
}// 使用示例
public class Application {public static void main(String[] args) {FilterChain chain = new FilterChain();// 基本過濾器Filter authFilter = new AuthenticationFilter();Filter logFilter = new LoggingFilter();// 裝飾后的過濾器Filter decoratedFilter = new MetricsFilterDecorator(logFilter);chain.addFilter(authFilter).addFilter(decoratedFilter);// 模擬請求Request request = new Request("/api/data", "valid-token");Response response = new Response();chain.doFilter(request, response);}
}
實際應用場景:
- Web應用的安全過濾器鏈?
- 數據處理的管道式處理
- 日志和監控系統的攔截器鏈
3.2 避免過度設計
?3.2.1.何時使用設計模式
設計模式應該在以下情況下考慮使用:
- 代碼重復:多處出現相似代碼結構時,考慮使用模板方法、策略等模式消除重復
- 對象創建復雜:創建邏輯復雜或需要解耦時,使用工廠、建造者等創建型模式
- 系統結構復雜:類之間依賴關系復雜時,使用適配器、橋接等結構型模式
- 行為多變:算法或行為需要靈活切換時,使用策略、狀態等行為型模式
- 需要解耦:組件間需要松耦合時,使用觀察者、中介者等模式
判斷標準:
- 模式是否解決了當前的實際問題?
- 不使用模式會帶來什么維護成本?
- 模式的引入成本是否值得?
?3.2.2.模式濫用的危害
過度使用設計模式會導致:
- 代碼復雜度增加:不必要的抽象層和間接調用
- 可讀性降低:其他開發者難以理解過度設計的結構
- 維護困難:簡單的修改需要涉及多個類
- 性能損耗:額外的抽象層可能帶來性能開銷
常見濫用場景:
- 為簡單對象創建復雜工廠
- 使用策略模式替代簡單的條件判斷
- 在不必要的地方強制使用設計模式
?3.2.3.重構到模式的技巧
將現有代碼重構為設計模式的建議:
- 識別痛點:先找出代碼中的問題(重復、僵化、脆弱等)
- 小步重構:每次只應用一個模式解決一個具體問題
- 測試驅動:確保重構不會破壞現有功能
- 漸進式改進:從簡單實現開始,按需引入模式
重構原則:
- 優先組合而非繼承:使用對象組合實現靈活的行為變化
- 面向接口編程:依賴抽象而非具體實現
- 單一職責:每個類/方法只做一件事
- 開閉原則:對擴展開放,對修改關閉
通過合理應用設計模式組合和避免過度設計,可以構建出既靈活又不過度復雜的系統架構。關鍵在于根據實際需求權衡設計復雜度和靈活性。
第六部分:博主的話
博主用了近一個月的時間寫了這篇文章,從設計模式的概念,到各個設計模式的詳細案例來對每一個設計模式進行解讀,希望能夠和大家共同學習。好多同學看源碼就是一頭霧水,究其因就是因為里面用到了大量的實際模式,這也是開發源代碼在工程與哲學層面的一個思考和結合,我們要用好設計模式不容易,需要閱讀大量的源代碼,通過不斷地學習和運用,才能領悟到每一個設計的獨到之處。最后,排版粗糙請大家諒解,希望大家都能成為一位優秀的開發者。