文章目錄
- 一 代理模式
- 1.1 靜態代理
- 1.1.1 靜態代理的結構
- 1.1.2 靜態代理的特點
- 1.1.3 靜態代理的應用場景
- 1.1.4 靜態代理的案例代碼
- 1.2 JDK動態代理
- 1.2.1 JDK動態代理概述
- 1.2.2 JDK動態代理案例代碼
- 1.2.3 JDK動態代理的應用場景
- 1.2.4 JDK動態代理的特點
- 1.2.5 與創建型模式的區別
- 1.3 CGLIB動態代理
- 1.3.1 CGLIB動態代理概述
- 1.3.2 CHLIB動態代理代碼案例
- 1.4 CGLIB vs JDK 動態代理對比
- 1.5 代理模式的經典應用
- 1.6 代理模式的經典應用
- 二 裝飾器模式
- 2.1 裝飾器模式的核心組成
- 2.2 裝飾器模式的代碼案例
- 2.3 裝飾器模式的優點
- 2.4 裝飾器模式的適用場景
- 三 適配器模式
- 3.1 適配器模式的主要角色
- 3.2 適配器模式的兩種實現方式
- 3.3 適配器模式的代碼案例
- 3.3.1 目標接口 `Target`
- 3.3.2 被適配者 `Adaptee`
- 3.3.3 對象適配器 `ObjectAdapter`
- 3.3.4 類適配器 `ClassAdapter`(如果使用繼承)
- 3.3.5 客戶端代碼
- 3.4 適配器模式的應用場景
- 3.5 適配器模式的優缺點
- 四 組合模式
- 4.1 組合模式的主要角色
- 4.2 組合模式的UML 結構圖
- 4.3 組合模式的代碼案例
- 4.3.1 定義 `Component` 接口
- 4.3.2 實現 `Leaf` 類
- 4.3.3 實現 `Composite` 類
- 4.3.4 客戶端使用
- 4.4 組合模式的適用場景
- 4.5 組合模式的優缺點
- 五 門面模式
- 5.1 門面模式的核心思想
- 5.2 門面模式的主要角色
- 5.3 門面模式的代碼案例
- 5.4 門面模式的應用場景
- 5.5 門面模式的優缺點
- 六 橋接模式
- 6.1 橋接模式的主要角色
- 6.2 橋接模式的應用場景
- 6.3 橋接模式的代碼案例
- 6.4 橋接模式的優缺點
- 6.5 橋接模式的經典應用
- 七 享元模式
- 7.1 享元模式的主要角色
- 7.2享元模式的代碼實例
- 7.2.1 抽象享元類
- 7.2.2 具體享元類
- 7.2.3 享元工廠類
- 7.2.4 客戶端調用
- 7.3享元模式的優缺點
- 7.4 享元模式的適用場景
- 7.5 享元模式的經典應用

一 代理模式
- 代理模式的作用是通過代理對象來增強目標對象的功能。利用AOP橫切的思想。代理模式的實現方式有三種:靜態代理,動態代理(JDK動態代理和CGLIB動態代理)。
1.1 靜態代理
- 靜態代理是 Java 中一種常見的設計模式,屬于創建型模式的一種。它主要用于在不修改目標對象的前提下,通過代理對象增強目標對象的功能。
1.1.1 靜態代理的結構
- 接口(Subject):定義目標對象和代理對象的公共接口。
- 目標類(RealSubject):實現接口,提供核心功能。
- 代理類(Proxy):也實現接口,內部持有目標對象的引用,可以在調用目標對象的方法前后添加額外操作。
1.1.2 靜態代理的特點
-
優點:
- 符合開閉原則:不需要修改目標對象,就可以為其增加新的功能。
- 提高代碼解耦性:代理對象和目標對象職責清晰。
-
缺點:
- 每個接口都需要一個代理類,會導致類數量增加。
- 如果接口方法發生變化,代理類也需要同步修改。
1.1.3 靜態代理的應用場景
- 日志記錄、性能監控。
- 權限控制。
- 遠程調用等需要對目標對象進行封裝的場景。
1.1.4 靜態代理的案例代碼
- 定義接口
Subject
public interface Subject {void request();
}
- 實現目標類
RealSubject
public class RealSubject implements Subject {@Overridepublic void request() {System.out.println("RealSubject: 處理請求");}
}
- 創建代理類
Proxy
public class Proxy implements Subject {private Subject realSubject;public Proxy(Subject realSubject) {this.realSubject = realSubject;}@Overridepublic void request() {System.out.println("Proxy: 請求前的預處理");realSubject.request(); // 調用真實對象的方法System.out.println("Proxy: 請求后的收尾");}
}
- 使用示例
public class Client {public static void main(String[] args) {Subject realSubject = new RealSubject();Subject proxy = new Proxy(realSubject);proxy.request(); // 通過代理調用目標對象的方法}
}
1.2 JDK動態代理
- 靜態代理適用于目標對象較少且接口固定的場景。如果需要動態地為多個對象生成代理,可以使用 JDK 動態代理 或 CGLIB 動態代理。
1.2.1 JDK動態代理概述
- JDK動態代理是一種運行時動態生成代理類的技術,允許在不修改目標類的情況下增強其功能。它主要依賴于
java.lang.reflect.Proxy
和java.lang.reflect.InvocationHandler
接口。 Proxy
: 提供靜態方法用于創建代理實例。InvocationHandler
: 定義了代理對象調用方法時的處理邏輯。
1.2.2 JDK動態代理案例代碼
- 創建一個接口(如
MyInterface
)。 - 創建接口的實現類(如
MyInterfaceImpl
)。 - 實現
InvocationHandler
接口,定義攔截邏輯。 - 使用
Proxy.newProxyInstance()
方法生成代理對象。
// 接口類
public interface MyInterface {void doSomething();
}
// 實現類
public class MyInterfaceImpl implements MyInterface{@Overridepublic void doSomething() {System.out.println("Do something");}
}import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
//代理類
public class MyInvocationHandler implements InvocationHandler {private final Object target;public MyInvocationHandler(Object target) {this.target = target;}public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("Before method call");Object result = method.invoke(target, args);System.out.println("After method call");return result;}}
//測試類
public static void main(String[] args) {// 創建目標對象(真實主題),它是 MyInterface 接口的具體實現類MyInterface target = new MyInterfaceImpl();// 創建一個 InvocationHandler 實例,并將目標對象傳入,用于處理代理對象的方法調用InvocationHandler handler = new MyInvocationHandler(target);// 使用 Proxy.newProxyInstance() 方法創建一個代理對象// 參數說明:// 1. target.getClass().getClassLoader():指定類加載器,用來加載動態生成的代理類// 2. new Class<?>[] { MyInterface.class }:指定代理類要實現的接口列表,這里只有一個接口 MyInterface// 3. handler:指定代理對象的方法調用處理器,即當代理對象的方法被調用時,會轉交給這個 handler 來處理MyInterface proxy = (MyInterface) Proxy.newProxyInstance(target.getClass().getClassLoader(), // 類加載器// 要代理的接口 使用new Class<?>[] { MyInterface.class },或target.getClass().getInterfaces(),target.getClass().getInterfaces(),handler // 方法調用處理器);// 調用代理對象的方法,實際會被 InvocationHandler 的 invoke 方法攔截并處理proxy.doSomething();
}
1.2.3 JDK動態代理的應用場景
- AOP編程:如日志記錄、性能監控等。
- 遠程調用:如RMI(Remote Method Invocation)。
- 權限控制:在調用目標方法前進行權限驗證。
1.2.4 JDK動態代理的特點
- 基于接口:只能對接口進行代理,不能對類進行代理。
- 動態生成字節碼:在運行時動態生成代理類的字節碼。
- 靈活性高:可以在不修改目標類的情況下增強其功能。
1.2.5 與創建型模式的區別
- 創建型模式(如工廠模式、單例模式、建造者模式等)關注的是對象的創建方式,隱藏對象的構造細節。
- JDK動態代理屬于行為型模式,關注的是對象之間的交互和職責分配。
1.3 CGLIB動態代理
1.3.1 CGLIB動態代理概述
- CGLIB(Code Generation Library)是一個強大的字節碼生成庫,常用于在運行時動態生成類。它在Java中廣泛應用于AOP編程、代理模式實現、ORM框架等場景。
- CGLIB動態代理的設計結構與使用方式,屬于結構型設計模式的一種變體(非GoF經典23種之一,但基于代理模式的擴展)。
- CGLIB動態代理是基于繼承機制實現的代理方式,適用于沒有接口的類。它是Spring AOP底層的重要技術支撐之一,具有廣泛的工程應用價值。
- CGLIB動態代理的核心概念
-
Enhancer
類:- 是CGLIB中最核心的類,用來創建一個被代理類的子類。
- 通過設置回調(
Callback
),攔截方法調用。
-
MethodInterceptor
接口:- 用戶自定義的攔截器,必須實現
intercept()
方法。 - 在該方法中可以加入增強邏輯(如日志、事務、權限控制等)。
- 用戶自定義的攔截器,必須實現
-
被代理類(Target Class):
- 不需要實現接口,這是與JDK動態代理的主要區別。
- CGLIB通過繼承方式生成子類來實現代理。
1.3.2 CHLIB動態代理代碼案例
- 需要引入依賴(Maven):
<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.3.0</version></dependency>
- 業務代碼
public class UserService {public void addUser() {System.out.println("添加用戶");}
}
- 實現 MethodInterceptor
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;public class MyMethodInterceptor implements MethodInterceptor {@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("前置增強邏輯");Object result = proxy.invokeSuper(obj, args); // 調用父類原始方法System.out.println("后置增強邏輯");return result;}
}
- 使用 Enhancer 創建代理對象
import net.sf.cglib.proxy.Enhancer;public class CglibProxyDemo {public static void main(String[] args) {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(UserService.class); // 設置父類enhancer.setCallback(new MyMethodInterceptor()); // 設置攔截器UserService proxy = (UserService) enhancer.create(); // 創建代理對象proxy.addUser(); // 調用方法,觸發攔截邏輯}
}
- 添加虛擬接參數解決報錯
--add-opens java.base/java.lang=ALL-UNNAMED
5. 執行結果
前置增強邏輯
添加用戶
后置增強邏輯
1.4 CGLIB vs JDK 動態代理對比
特性 | CGLIB動態代理 | JDK動態代理 |
---|---|---|
原理 | 繼承目標類生成子類 | 實現接口生成代理 |
是否需要接口 | 否 | 是 |
性能 | 初次生成較慢,運行快 | 每次反射調用較慢 |
應用場景 | Spring AOP(無接口)、Hibernate懶加載等 | Spring AOP(有接口)、RMI |
1.5 代理模式的經典應用
- Spring中Bean對象的AOP代理。
- MyBatis中Mapper接口代理對象。
- MyBatis中JDBC的日志代理。
- Ribbon或Loadbalancer中的RestTemplate代理對象。
- OpenFeign的接口代理對象。
1.6 代理模式的經典應用
- 代理模式在業務系統中的應用一般都是用來拓展增強業務功能。現在需要統計每個請求到來的處理時長,如果超過500毫秒就給出警告。
//控制層
public class UserController {private UserService userService;public UserController() {this.userService = new UserService(); // 改為構造函數中初始化}public String login() {return userService.login();}
}
// 服務層
public class UserService {public String login(){return "登錄成功";}
}
//測試類
public class StartApp {public static void main(String[] args) {UserController userController = new UserController();UserController proxy = new MethodInterceptor() {public UserController createProxy(Class targetClass) {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(targetClass);enhancer.setCallback(this);return (UserController) enhancer.create();}@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {long start = System.currentTimeMillis();Object res = proxy.invoke(userController, args);long end = System.currentTimeMillis();System.out.println("耗時:" + (end - start));if(end-start>500){System.out.println("耗時過長");}return res;}}.createProxy(UserController.class);proxy.login();}
}
二 裝飾器模式
- 裝飾器模式(Decorator Pattern/包裝模式)是一種結構型設計模式,它允許你通過將對象放入包含行為的特殊封裝對象中來為原對象增加功能。這種模式比靜態子類化更靈活,能夠在運行時動態地給對象添加職責。
- 裝飾模式是一種用于代替繼承的技術,無需通過繼承子類增加子類就能拓展對象的新功能。使用對象的關聯關系代替繼承關系,更加靈活、同時避免類型體系的快速膨脹。
2.1 裝飾器模式的核心組成
- Component(抽象組件):定義對象和裝飾器的公共接口。
- ConcreteComponent(具體組件):實現基礎功能的對象。
- Decorator(抽象裝飾器):繼承或實現
Component
,并持有Component
對象的引用。 - ConcreteDecorator(具體裝飾器):為對象增加具體的功能。
2.2 裝飾器模式的代碼案例
- 抽象組件
Coffee
public interface Coffee {double cost();String description();
}
- 具體組件
SimpleCoffee
public class SimpleCoffee implements Coffee {@Overridepublic double cost() {return 2.0;}@Overridepublic String description() {return "Simple Coffee";}
}
- 抽象裝飾器
CoffeeDecorator
public abstract class CoffeeDecorator implements Coffee {protected Coffee decoratedCoffee;public CoffeeDecorator(Coffee coffee) {this.decoratedCoffee = coffee;}@Overridepublic double cost() {return decoratedCoffee.cost();}@Overridepublic String description() {return decoratedCoffee.description();}
}
- 具體裝飾器
MilkDecorator
public class MilkDecorator extends CoffeeDecorator {public MilkDecorator(Coffee coffee) {super(coffee);}@Overridepublic double cost() {return super.cost() + 0.5; // 加牛奶的價格}@Overridepublic String description() {return super.description() + ", Milk";}
}
- 具體裝飾器
SugarDecorator
public class SugarDecorator extends CoffeeDecorator {public SugarDecorator(Coffee coffee) {super(coffee);}@Overridepublic double cost() {return super.cost() + 0.2; // 加糖的價格}@Overridepublic String description() {return super.description() + ", Sugar";}
}
- 使用示例
public class Main {public static void main(String[] args) {Coffee coffee = new SimpleCoffee();System.out.println("Cost: $" + coffee.cost() + " | Description: " + coffee.description());Coffee milkCoffee = new MilkDecorator(new SimpleCoffee());System.out.println("Cost: $" + milkCoffee.cost() + " | Description: " + milkCoffee.description());Coffee sugarMilkCoffee = new SugarDecorator(new MilkDecorator(new SimpleCoffee()));System.out.println("Cost: $" + sugarMilkCoffee.cost() + " | Description: " + sugarMilkCoffee.description());}
}
- 輸出結果
Cost: $2.0 | Description: Simple Coffee
Cost: $2.5 | Description: Simple Coffee, Milk
Cost: $2.7 | Description: Simple Coffee, Milk, Sugar
2.3 裝飾器模式的優點
- 靈活性高:相比靜態繼承方式,裝飾器模式更加靈活,可以在運行時動態地組合對象和功能。
- 開閉原則:無需修改原有代碼即可擴展功能。
- 組合優于繼承:避免了類爆炸的問題,多個裝飾器可以自由組合使用。
2.4 裝飾器模式的適用場景
- 當需要動態、透明地給對象添加職責時。
- 當子類擴展不切實際或會導致類爆炸時。
- 當希望保持類責任清晰,并避免復雜的繼承關系時。
三 適配器模式
- 適配器模式(Adapter Pattern)是一種結構型設計模式,它允許將一個類的接口轉換成客戶端期望的另一個接口。適配器模式常用于解決兩個不兼容接口之間的適配問題。
- 適配器模式的作用是把兩個不兼容的對象通過適配器連接起來工作。
- 在 Java 中,
java.util.Arrays.asList()
可以將數組轉換為List
,這其實也是一種適配器模式。 - Spring 框架中也廣泛使用適配器模式來適配不同的事件監聽器、處理器等組件。
3.1 適配器模式的主要角色
- 目標接口(Target):定義客戶端使用的接口。
- 被適配者(Adaptee):需要被適配的現有類,其接口與目標接口不兼容。
- 適配器(Adapter):實現目標接口,并持有被適配者的實例,通過組合或繼承的方式完成接口轉換。
3.2 適配器模式的兩種實現方式
- 對象適配器(推薦):使用組合的方式,適配器包含被適配者的實例。
- 類適配器:使用繼承的方式,適配器繼承自被適配者并實現目標接口。
3.3 適配器模式的代碼案例
3.3.1 目標接口 Target
public interface Target {void request();
}
3.3.2 被適配者 Adaptee
public class Adaptee {public void specificRequest() {System.out.println("Adaptee's specific request.");}
}
3.3.3 對象適配器 ObjectAdapter
public class ObjectAdapter implements Target {private Adaptee adaptee;public ObjectAdapter(Adaptee adaptee) {this.adaptee = adaptee;}@Overridepublic void request() {adaptee.specificRequest();}
}
3.3.4 類適配器 ClassAdapter
(如果使用繼承)
public class ClassAdapter extends Adaptee implements Target {@Overridepublic void request() {specificRequest();}
}
3.3.5 客戶端代碼
public class Client {public static void main(String[] args) {// 使用對象適配器Adaptee adaptee = new Adaptee();Target target = new ObjectAdapter(adaptee);target.request(); // 輸出: Adaptee's specific request.// 使用類適配器Target classAdapter = new ClassAdapter();classAdapter.request(); // 輸出: Adaptee's specific request.}
}
3.4 適配器模式的應用場景
- 當希望復用已有的類,但其接口不符合當前需求時。
- 當需要在不影響現有代碼的情況下擴展功能時。
- 當多個子類有不同的接口,而客戶端希望統一調用時。
3.5 適配器模式的優缺點
優點
- 提高了類的復用性,避免對已有代碼進行修改。
- 解耦客戶端和被適配者,使得兩者可以獨立變化。
- 符合開閉原則,新增適配器不會影響原有系統。
缺點
- 增加系統的復雜度,引入額外的適配類。
- 如果過度使用,可能導致系統難以理解和維護。
四 組合模式
- 組合模式(Composite Pattern)是一種結構型設計模式,它允許你將對象組合成樹形結構來表示“部分-整體”的層次關系。通過組合模式,客戶端可以統一地處理單個對象和對象的組合。
- 組合模式的核心在于統一處理葉子節點(Leaf)和組合節點(Composite)。它讓客戶端無需關心當前操作的是單個對象還是組合對象,從而簡化了客戶端代碼。
- 組合模式非常適合用于構建具有遞歸結構的對象樹,尤其在需要統一處理個體和群體的情況下非常有用。
4.1 組合模式的主要角色
Component
:抽象類或接口,定義葉子節點和組合節點的公共行為。Leaf
:表示葉子節點,沒有子節點,實現基礎功能。Composite
:表示組合節點,包含子組件(可以是葉子節點或其他組合節點),并實現了管理子組件的方法(如添加、移除等)。
4.2 組合模式的UML 結構圖
+-----------+| Component |+-----------+| operation()|+-----------+/ \/ \/ \
+--------+ +-------------+
| Leaf | | Composite |
+----------+ +-------------+
| operation()| | operation() || add(Component)|| remove(Component)|| getChild(int) |+---------------+
4.3 組合模式的代碼案例
4.3.1 定義 Component
接口
public interface Component {void operation();
}
4.3.2 實現 Leaf
類
public class Leaf implements Component {private String name;public Leaf(String name) {this.name = name;}@Overridepublic void operation() {System.out.println("Leaf " + name + " is doing operation.");}
}
4.3.3 實現 Composite
類
import java.util.ArrayList;
import java.util.List;public class Composite implements Component {private String name;private List<Component> children = new ArrayList<>();public Composite(String name) {this.name = name;}public void add(Component component) {children.add(component);}public void remove(Component component) {children.remove(component);}@Overridepublic void operation() {System.out.println("Composite " + name + " is doing operation.");for (Component child : children) {child.operation();}}
}
4.3.4 客戶端使用
public class Client {public static void main(String[] args) {// 創建葉子節點Component leaf1 = new Leaf("Leaf1");Component leaf2 = new Leaf("Leaf2");// 創建組合節點Composite composite = new Composite("Composite1");composite.add(leaf1);composite.add(leaf2);// 調用組合的操作composite.operation();}
}
- 輸出結果
Composite Composite1 is doing operation.
Leaf Leaf1 is doing operation.
Leaf Leaf2 is doing operation.
4.4 組合模式的適用場景
- 表示“部分-整體”的樹形結構。
- 希望客戶端能夠忽略組合對象與單個對象的不同,統一處理它們。
- 需要動態地構建層級結構,并希望靈活地增刪結構中的元素。
4.5 組合模式的優缺點
優點
- 符合開閉原則,增加新的組件類不需要修改現有代碼。
- 簡化客戶端代碼,使客戶端更容易操作復雜結構。
- 提供一致的方式處理單個對象和組合對象。
缺點
- 如果過度使用組合模式,可能會導致系統中出現大量細小的對象,增加調試和維護成本。
- 對于不支持樹形結構的操作,需要額外處理。
五 門面模式
- 門面模式(Facade Pattern/外觀模式)是 Java 中常用的 結構型設計模式 之一,它的主要目的是為子系統中的一組接口提供一個統一的高層接口,使得子系統更容易被使用。
- 門面模式是一種非常實用的設計模式,特別適用于需要將復雜的子系統簡化為一個統一接口的場景。它通過引入一個外觀類,將客戶端與子系統的復雜性隔離開來,從而提高了系統的可維護性和易用性。
5.1 門面模式的核心思想
- 簡化復雜系統的調用方式:通過引入一個外觀類(Facade),將復雜的調用流程封裝起來,讓客戶端只需要與外觀類交互。
- 降低耦合度:客戶端不直接依賴子系統的具體實現,而是通過外觀類間接訪問,提高了模塊化和可維護性。
5.2 門面模式的主要角色
- 子系統類(SubSystem):實現了系統內部的具體功能。
- 外觀類(Facade):提供一個統一的接口,封裝子系統的調用邏輯。
- 客戶端(Client):使用外觀類來完成對子系統的操作。
5.3 門面模式的代碼案例
// 子系統類 A
class SubSystemA {public void operationA() {System.out.println("SubSystemA: 操作 A");}
}// 子系統類 B
class SubSystemB {public void operationB() {System.out.println("SubSystemB: 操作 B");}
}// 子系統類 C
class SubSystemC {public void operationC() {System.out.println("SubSystemC: 操作 C");}
}// 外觀類
class Facade {private SubSystemA subSystemA;private SubSystemB subSystemB;private SubSystemC subSystemC;public Facade() {this.subSystemA = new SubSystemA();this.subSystemB = new SubSystemB();this.subSystemC = new SubSystemC();}// 高層接口public void operation() {subSystemA.operationA();subSystemB.operationB();subSystemC.operationC();}
}// 客戶端
public class Client {public static void main(String[] args) {Facade facade = new Facade();facade.operation(); // 調用外觀類的方法}
}
- 輸出結果
SubSystemA: 操作 A
SubSystemB: 操作 B
SubSystemC: 操作 C
5.4 門面模式的應用場景
- 簡化接口:當需要隱藏復雜的子系統時,可以通過門面模式提供一個更簡單的接口。
- 分層架構設計:在分層系統中,每一層通過門面暴露其服務給上層。
- 遺留系統封裝:當需要集成舊系統時,可以使用門面模式將其封裝成一個新接口。
- 解耦:減少客戶端與子系統的直接依賴,提高代碼的靈活性。
5.5 門面模式的優缺點
優點
- 簡化客戶端調用邏輯。
- 降低系統間的耦合度。
- 更容易維護和擴展。
缺點
- 不符合開閉原則:如果新增子系統或修改現有子系統,可能需要修改外觀類的代碼。
六 橋接模式
- 橋接模式(Bridge Pattern)是一種結構型設計模式,用于將抽象部分與其具體實現部分分離,使它們可以獨立變化。它通過組合的方式替代繼承,避免類爆炸的問題。
6.1 橋接模式的主要角色
- 橋接模式包含以下核心角色:
-
Abstraction(抽象類)
- 定義抽象類的接口,并持有對
Implementor
的引用。 - 通常是一個高層次的抽象,不直接實現功能,而是委托給
Implementor
。
- 定義抽象類的接口,并持有對
-
RefinedAbstraction(擴展抽象類)
- 擴展
Abstraction
類的功能,增加更復雜的業務邏輯。
- 擴展
-
Implementor(實現接口)
- 定義實現部分的接口,供
Abstraction
調用。 - 通常是不同平臺或方式下的統一接口。
- 定義實現部分的接口,供
-
ConcreteImplementorA / ConcreteImplementorB(具體實現類)
- 實現
Implementor
接口的具體行為。
- 實現
6.2 橋接模式的應用場景
- 避免在兩個獨立維度上使用多重繼承導致類數量爆炸。
- 抽象和其實現都要通過子類擴展時,希望解耦兩者的繼承關系。
- 運行時可以切換實現的情況。
6.3 橋接模式的代碼案例
// Implementor
interface Implementor {void operationImpl();
}// ConcreteImplementorA
class ConcreteImplementorA implements Implementor {public void operationImpl() {System.out.println("ConcreteImplementorA operation");}
}// ConcreteImplementorB
class ConcreteImplementorB implements Implementor {public void operationImpl() {System.out.println("ConcreteImplementorB operation");}
}// Abstraction
abstract class Abstraction {protected Implementor implementor;protected Abstraction(Implementor implementor) {this.implementor = implementor;}public abstract void operation();
}// RefinedAbstraction
class RefinedAbstraction extends Abstraction {public RefinedAbstraction(Implementor implementor) {super(implementor);}public void operation() {System.out.print("RefinedAbstraction: ");implementor.operationImpl();}
}//測試類
public class Client {public static void main(String[] args) {Implementor implA = new ConcreteImplementorA();Implementor implB = new ConcreteImplementorB();Abstraction abstrA = new RefinedAbstraction(implA);Abstraction abstrB = new RefinedAbstraction(implB);abstrA.operation(); // 輸出:RefinedAbstraction: ConcreteImplementorA operationabstrB.operation(); // 輸出:RefinedAbstraction: ConcreteImplementorB operation}
}
6.4 橋接模式的優缺點
優點
- 解耦抽象與實現:允許兩者獨立變化。
- 提高可擴展性:新增一個維度只需擴展,不需修改。
- 避免類爆炸問題:通過組合代替繼承。
缺點
- 增加系統理解難度:需要正確識別兩個獨立變化的維度。
- 接口設計要求高:必須提前規劃好
Implementor
和Abstraction
的接口。
6.5 橋接模式的經典應用
- 不同操作系統上的圖形繪制庫(圖形是抽象,渲染是實現)。
- 多種支付渠道(微信、支付寶)與多種支付方式(掃碼、刷臉)的組合。
- 消息通知系統:消息類型(文本、圖片)與發送渠道(短信、郵件、站內信)的組合。
- JDBC(java與數據庫交互的API)將java應用程序與不同數據庫之間的連接進行解耦。JDBC提供標準的接口作為抽象部分,具體的數據庫驅動程序作為實現部分。
- SLF4J日志框架提供Logger,Appender和Formatter。他們三個表示不同的維度。Logger表示日志記錄所屬的類,Appender表示日志的輸出,Formatter表示日志記錄的格式。三個維度可以有多種不同的實現,利用橋接模式,可以實現三種維度的任意組合。
七 享元模式
- 享元模式(Flyweight Pattern)是 Java 中常用的結構型設計模式之一,主要用于減少創建和管理大量相似對象時的內存開銷。
- 享元模式的核心思想:通過共享技術來有效支持大量細粒度的對象的重用。適用于:對象數量巨大;大部分對象的狀態可以外部化(即對象之間差異小)
- 享元對象能做到共享的關鍵是區分了內部狀態和外部狀態。
- 內部狀態:可以共享,不會隨環境的改變而變化
- 外部狀態:不可以共享,會隨環境的改變而變化
- 例如象棋上棋子的顏色,形狀,大小是共享屬性(內部狀態),而位置是不可共享屬性(外部狀態)。
7.1 享元模式的主要角色
角色 | 描述 |
---|---|
Flyweight (抽象享元類) | 定義公共接口或抽象類,包含一個operation()方法。 |
ConcreteFlyweight (具體享元類) | 實現抽象享元接口,存儲內部狀態(intrinsic state)。 |
UnsharedConcreteFlyweight (非共享享元類) | 不參與共享的享元類,通常用于組合結構。 |
FlyweightFactory (享元工廠類) | 負責創建和管理享元對象,通常使用 Map 緩存已創建的享元對象。 |
7.2享元模式的代碼實例
7.2.1 抽象享元類
public interface Flyweight {void operation(String extrinsicState); // extrinsicState 是外部狀態
}
7.2.2 具體享元類
public class ConcreteFlyweight implements Flyweight {private String intrinsicState; // 內部狀態,可共享public ConcreteFlyweight(String intrinsicState) {this.intrinsicState = intrinsicState;}@Overridepublic void operation(String extrinsicState) {System.out.println("內部狀態: " + intrinsicState + ", 外部狀態: " + extrinsicState);}
}
7.2.3 享元工廠類
import java.util.HashMap;
import java.util.Map;public class FlyweightFactory {private Map<String, Flyweight> flyweights = new HashMap<>();public Flyweight getFlyweight(String key) {if (!flyweights.containsKey(key)) {flyweights.put(key, new ConcreteFlyweight(key));}return flyweights.get(key);}public int getTotalFlyweights() {return flyweights.size();}
}
7.2.4 客戶端調用
public class Client {public static void main(String[] args) {FlyweightFactory factory = new FlyweightFactory();Flyweight f1 = factory.getFlyweight("A");Flyweight f2 = factory.getFlyweight("B");Flyweight f3 = factory.getFlyweight("A"); // 共享已有對象f1.operation("X");f2.operation("Y");f3.operation("Z");System.out.println("實際創建的享元對象數量:" + factory.getTotalFlyweights());}
}
- 輸出結果
內部狀態: A, 外部狀態: X
內部狀態: B, 外部狀態: Y
內部狀態: A, 外部狀態: Z
實際創建的享元對象數量:2
7.3享元模式的優缺點
優點
- 節省內存:通過共享對象,減少重復創建對象的數量。
- 提高性能:避免頻繁的對象創建與銷毀。
缺點
- 對JVM回收不友好,因為工廠類一直保持對享元類的引用,造成享元類在沒有任何引用的情況下也不會被JVM回收。
7.4 享元模式的適用場景
- 系統中存在大量相似對象,如字符、圖形、連接池等。需要將對象的狀態分為內部狀態和外部狀態:
- 內部狀態:不變且可共享(如字體名稱)
- 外部狀態:隨環境變化而變化(如字體大小、顏色)
7.5 享元模式的經典應用
- 文本編輯器中對字符的處理(每個字符作為享元對象)
- 游戲開發中的粒子系統,棋類游戲的棋子對象
- 數據庫連接池、線程池等資源池化管理