当前位置: 首页 > news >正文

模式组合应用-代理模式

写在前面

Hello,我是易元,这篇文章是我学习设计模式时的笔记和心得体会。如果其中有错误,欢迎大家留言指正!


代理模式

定义

结构型设计模式, 为一个对象提供一个替身或占位符, 进而控制对这个对象的访问。

通过引入一个代理对象, 将客户端对真实对象的直接访问进行封装和控制, 从而在不改变真实对象的情况下, 增加额外的功能或限制。

角色

  • 抽象主题: 定义了真实主题和代理主题的共同接口, 使得在任何可以使用真实主题的地方都可以使用代理主题。

  • 真实主题: 定义了代理所代表的真实对象, 它包含了业务逻辑, 是代理模式最终要访问的对象。

  • 代理主题: 持有对真实主题的引用, 并实现与真实主题相同的接口, 代理对象可以在将请求转发给真实主题之前或之后, 执行一些预处理或 后处理的逻辑。

使用时主要思考点

  1. 明确代理目的: 在引入代理模式前, 首先要明确代理的目的是什么, 如控制访问(权限控制、远程代理等)、增强功能(如 日志、缓存、事务), 不同的目的可能导致代理的实现方式和复杂程度不同。

  2. 接口一致性: 代理对象和真实对象必须实现相同的接口。

  3. 性能开销: 引入代理会增加一层间接性, 这可能会带来一定的性能开销, 在对性能要求极高的场景下, 需要仔细评估。

  4. 避免过度设计: 一些简单功能可以通过直接调用实现, 引入代理会使系统变得过于复杂。


JDK 动态代理

JDK动态代理是Java语言自动的代理机制, 基于接口实现。这意味着只有实现了接口的类才能使用JDK动态代理。

核心接口

  • InvocationHandler 接口: 代理实例的调用处理程序实现的接口。每个代理实例都具有一个关联的调用处理程序。当代理实例上调用方法时, 方法调用将被编码并分派到其调用处理程序的 invoke 方法。

  • Proxy类: 提供了创建动态代理类和实例的静态方法。

代码示例

抽象主题
public interface UserService {void registerUser(String username, String password);void login(String username, String password);}
真实主题
public class UserServiceImpl implements UserService {@Overridepublic void registerUser(String username, String password) {System.out.println("开始执行用户注册业务逻辑: " + username);if (username != null && !username.isEmpty() && password != null && !password.isEmpty()) {System.out.println("用户 " + username + " 注册成功!");}else {System.out.println("用户注册失败: 用户名或密码不能为空。");}System.out.println("用户注册业务逻辑执行完毕。");}@Overridepublic void login(String username, String password) {System.out.println("开始执行用户登陆业务逻辑: " + username);if ("admin".equals(username) && "123".equals(password)) {System.out.println("用户 " + username + " 登录成功!");}else {System.out.println("用户 " + username + " 登录失败: 用户名或密码错误。");}System.out.println("用户登录业务逻辑执行完毕。");}}
动态代理处理器
public class PerformanceLogInvocationHandler implements InvocationHandler {private Object target;public PerformanceLogInvocationHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {long startTime = System.currentTimeMillis();System.out.println("\n--- 动态代理前置处理: 方法 " + method.getName() + "开始执行, 参数: " + Arrays.toString(args) + " ---");Object result = null;try {// 调用真实对象的方法result = method.invoke(target, args);} catch (Exception e) {System.out.println("动态代理异常处理: 方法 " + method.getName() + " 执行异常: " + e.getMessage());throw e.getCause();} finally {long endTime = System.currentTimeMillis();System.out.println("--- 动态代理后置处理: 方法 " + method.getName() + " 执行完毕, 耗时: " + (endTime - startTime) + " 毫秒 ---");System.out.println("操作日志: 方法 " + method.getName() + " 在 " + new Date() + " 被调用。");}return result;}
}
测试类
public class JdkDynamicProxyTest {@Testpublic void test_jdkProxy() {System.out.println("--- 使用 JDK 动态代理进行用户服务操作 ---");UserService realUserService = new UserServiceImpl();Class<? extends UserService> aClass = realUserService.getClass();UserService proxyUserService = (UserService) Proxy.newProxyInstance(aClass.getClassLoader(), // 类加载器aClass.getInterfaces(), // 真实对象实现的接口器new PerformanceLogInvocationHandler(realUserService) // 调用处理器);System.out.println("\n--- 第一次操作: 注册用户 ---");proxyUserService.registerUser("赵六", "789012");System.out.println("\n--- 第二次操作: 登录用户 ---");proxyUserService.login("admin", "123");System.out.println("\n--- 第三次操作: 登录失败 ---");proxyUserService.login("guest", "wrongpass");}}
运行结果
--- 使用 JDK 动态代理进行用户服务操作 ------ 第一次操作: 注册用户 ------ 动态代理前置处理: 方法 registerUser开始执行, 参数: [赵六, 789012] ---
开始执行用户注册业务逻辑: 赵六
用户 赵六 注册成功!
用户注册业务逻辑执行完毕。
--- 动态代理后置处理: 方法 registerUser 执行完毕, 耗时: 0 毫秒 ---
操作日志: 方法 registerUser 在 Mon Sep 29 10:35:32 CST 2025 被调用。--- 第二次操作: 登录用户 ------ 动态代理前置处理: 方法 login开始执行, 参数: [admin, 123] ---
开始执行用户登陆业务逻辑: admin
用户 admin 登录成功!
用户登录业务逻辑执行完毕。
--- 动态代理后置处理: 方法 login 执行完毕, 耗时: 0 毫秒 ---
操作日志: 方法 login 在 Mon Sep 29 10:35:32 CST 2025 被调用。--- 第三次操作: 登录失败 ------ 动态代理前置处理: 方法 login开始执行, 参数: [guest, wrongpass] ---
开始执行用户登陆业务逻辑: guest
用户 guest 登录失败: 用户名或密码错误。
用户登录业务逻辑执行完毕。
--- 动态代理后置处理: 方法 login 执行完毕, 耗时: 0 毫秒 ---
操作日志: 方法 login 在 Mon Sep 29 10:35:32 CST 2025 被调用。Process finished with exit code 0

CGLIB 动态代理

CGLIB是一个字节码生成库, 它可以在运行时扩展 Java类和实现 Java 接口。CGLIB通过继承方式实现代理, 它会生成一个真实对象的子类作为代理类, 因此可以代理没有实现接口的类。

核心接口

  • MethodInterceptor : 接口通过唯一的 intercept方法实现对代理对象方法调用的拦截。核心是通过 MethodProxy.invokeSuper()调用目标类的原始方法,并在前后插入自定义逻辑。

注意事项

  • 必须通过 proxy.invokeSuper(obj, args) 调用目标类的原始方法, method.invoke(obj,args) 会使对象再次触发连接器。

代码示例

真实主题
public class ConfigurationService {private Map<String, String> configCache = new HashMap<>();public String getConfig(String key) {System.out.println("正在从数据源加载配置项: " + key + "...");try {Thread.sleep(100);} catch (InterruptedException e) {Thread.currentThread().interrupt();}String value = "配置值_" + key + "_" + System.currentTimeMillis();System.out.println("加载完成, 配置项 " + key + " 的值为: " + value);return value;}public void updateConfig(String key, String value) {System.out.println("正在更新配置项: " + key + " 为 " + value + "...");System.out.println("配置项 " + key + " 更新成功。");}
}
CGLIB 动态代理拦截器
public class CacheAndLogMethodInterceptor implements MethodInterceptor {private Object target;private Map<String, Object> cache = new HashMap<>();public CacheAndLogMethodInterceptor(Object target) {this.target = target;}@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("\n--- CGLIB 代理前置处理: 方法 " + method.getName() + " 被调用, 参数: " + Arrays.toString(args) + " ---");if (method.getName().equals("getConfig") && args.length == 1 && args[0] instanceof String) {String key = (String) args[0];if (cache.containsKey(key)) {System.out.println("CGLIB 代理: 从缓存中获取配置项: " + key);return cache.get(key);}}Object result = null;try {result = proxy.invokeSuper(obj, args);if (method.getName().equals("getConfig") && args.length == 1 && args[0] instanceof String && result != null) {String key = (String) args[0];cache.put(key, result);System.out.println("CGLIB 代理: 将配置项 " + key + " 放入缓存。");}} catch (Exception e) {System.out.println("CGLIB 代理异常处理: 方法 " + method.getName() + " 执行异常: " + e.getMessage());throw e.getCause();} finally {System.out.println("--- CGLIB 代理后置处理: 方法 " + method.getName() + " 执行完毕。---");}return result;}}
测试类
public class CglibDynamicProxyTest {@Testpublic void test_cglib() {System.out.println("--- 使用 CGLIB 动态代理进行配置服务操作 ---");ConfigurationService realConfigService = new ConfigurationService();Enhancer enhancer = new Enhancer();enhancer.setSuperclass(ConfigurationService.class); // 设置父类 即代理的类enhancer.setCallback(new CacheAndLogMethodInterceptor(realConfigService)); // 设置回调函数ConfigurationService proxyConfigService = (ConfigurationService) enhancer.create(); // 创建代理对象System.out.println("\n--- 第一次获取配置: config_key_A ---");proxyConfigService.getConfig("config_key_A");System.out.println("\n--- 第二次获取配置: config_key_A(从缓存中获取) ---");proxyConfigService.getConfig("config_key_A");System.out.println("\n--- 第三次获取配置: config_key_B ---");proxyConfigService.getConfig("config_key_B");System.out.println("\n--- 第四次更新配置: config_key_A ---");proxyConfigService.updateConfig("config_key_A", "new_value_A");System.out.println("\n--- 第五次获取配置: config_key_A(更新后应重新加载) ---");proxyConfigService.getConfig("config_key_A");}}
运行结果
--- 使用 CGLIB 动态代理进行配置服务操作 ------ 第一次获取配置: config_key_A ------ CGLIB 代理前置处理: 方法 getConfig 被调用, 参数: [config_key_A] ---
正在从数据源加载配置项: config_key_A...
加载完成, 配置项 config_key_A 的值为: 配置值_config_key_A_1759113258603
CGLIB 代理: 将配置项 config_key_A 放入缓存。
--- CGLIB 代理后置处理: 方法 getConfig 执行完毕。------ 第二次获取配置: config_key_A(从缓存中获取) ------ CGLIB 代理前置处理: 方法 getConfig 被调用, 参数: [config_key_A] ---
CGLIB 代理: 从缓存中获取配置项: config_key_A--- 第三次获取配置: config_key_B ------ CGLIB 代理前置处理: 方法 getConfig 被调用, 参数: [config_key_B] ---
正在从数据源加载配置项: config_key_B...
加载完成, 配置项 config_key_B 的值为: 配置值_config_key_B_1759113258715
CGLIB 代理: 将配置项 config_key_B 放入缓存。
--- CGLIB 代理后置处理: 方法 getConfig 执行完毕。------ 第四次更新配置: config_key_A ------ CGLIB 代理前置处理: 方法 updateConfig 被调用, 参数: [config_key_A, new_value_A] ---
正在更新配置项: config_key_A 为 new_value_A...
配置项 config_key_A 更新成功。
--- CGLIB 代理后置处理: 方法 updateConfig 执行完毕。------ 第五次获取配置: config_key_A(更新后应重新加载) ------ CGLIB 代理前置处理: 方法 getConfig 被调用, 参数: [config_key_A] ---
CGLIB 代理: 从缓存中获取配置项: config_key_AProcess finished with exit code 0

代理模式+装饰器模式

装饰器模式

结构型设计模式, 允许在不改变原有对象结构的情况下, 动态地给对象添加新的功能。

通过创建一个包装对象, 来包裹真实的对象, 并在不改变其接口的前提下增强其功能。

案例

假设正在开发一个文件管理系统, 用户可以对文件进行读写操作。对于不同的文件或不同的用户, 我们可能需要动态地组合多种文件操作功能。例如:

  1. 权限控制: 只有特定用户才能访问某些文件。

  2. 数据加密: 敏感文件在写入时需要加密, 读取时需要解密。

  3. 数据压缩: 大文件在传输或存储时需要压缩, 读取时需要解压。

模式职责

  • 代理模式: 作为文件操作的入口, 提供基础的访问控制或预处理, 例如检查文件是否存在、用户是否有权限进行操作等, 确保了文件操作的合法性。

  • 装饰器模式: 动态地添加额外的功能, 如加密、解密、压缩、解压缩等。

代码示例

抽象组件/抽象主题
public interface FileOperation {void write(String filePath, String content);String read(String filePath);}
  • 定义了文件读写操作的抽象, 是代理和装饰器模式共同的接口, 确保了它们可以相互兼容和组合。

具体组件/真实主题
public class RealFileOperation implements FileOperation {private String fileContent = "";@Overridepublic void write(String filePath, String content) {System.out.println("\n[真实文件操作] 正在将内容写入文件: " + filePath);this.fileContent = content;System.out.println("写入成功, 文件内容为: " + content);}@Overridepublic String read(String filePath) {System.out.println("\n[真实文件操作] 正在从文件读取内容: " + filePath);System.out.println("读取成功, 文件内容为: " + fileContent);return fileContent;}
}
  • 实现了 FileOperation 接口, 负责模拟实际的文件读写操作, 不包含任何权限、加密或压缩逻辑。

代理主题
public class FileAccessProxy implements FileOperation {/*** 持有实际操作文件类的引用*/private RealFileOperation realFileOperation;private String currentUser;public FileAccessProxy(String currentUser) {this.realFileOperation = new RealFileOperation();this.currentUser = currentUser;}private boolean checkPermission(String filePath, String operationType) {if (filePath.contains("sensitive") && "write".equals(operationType) && !"admin".equals(currentUser)) {System.out.println("[文件访问代理] 权限不足: 用户 " + currentUser + " 无权写入敏感文件: " + filePath);return false;}System.out.println("[文件访问代理] 权限检查通过: 用户 " + currentUser + " 可以执行 " + operationType + " 操作。");return true;}@Overridepublic void write(String filePath, String content) {if (this.checkPermission(filePath, "write")) {realFileOperation.write(filePath, content);}}@Overridepublic String read(String filePath) {if (this.checkPermission(filePath, "read")) {return realFileOperation.read(filePath);}return "";}
}
  • 实现了 FileOperation 接口, 并持有一个 RealFileOperation 实例。主要职责是在调用真实文件操作之前, 进行用户权限的检查。如果权限不足则阻止操作;否则,将请求转发给真实文件操作类。

抽象装饰器类
public abstract class FileOperationDecorator implements FileOperation {protected FileOperation decoratedFileOperation;public FileOperationDecorator(FileOperation decoratedFileOperation) {this.decoratedFileOperation = decoratedFileOperation;}@Overridepublic void write(String filePath, String content) {decoratedFileOperation.write(filePath, content);}@Overridepublic String read(String filePath) {return decoratedFileOperation.read(filePath);}}
  • 实现了 FileOperation 接口, 并包含一个 FileOperation 类型的成员变量, 用于引用被装饰的对象, 默认将所有方法调用转发给被装饰的对象。

具体装饰器类
public class EncryptionDecorator extends FileOperationDecorator {public EncryptionDecorator(FileOperation decoratedFileOperation) {super(decoratedFileOperation);}private String encrypt(String data) {System.out.println("[加密装饰器] 正在加密数据...");return Base64.getEncoder().encodeToString(data.getBytes(StandardCharsets.UTF_8));}private String decrypt(String encryptedData) {System.out.println("[加密装饰器] 正在解密数据...");return new String(Base64.getDecoder().decode(encryptedData), StandardCharsets.UTF_8);}@Overridepublic void write(String filePath, String content) {String encryptedContent = this.encrypt(content);System.out.println("[加密装饰器] 加入加密后的内容。");decoratedFileOperation.write(filePath, encryptedContent);}@Overridepublic String read(String filePath) {String encryptedContent = decoratedFileOperation.read(filePath);if (encryptedContent != null && !encryptedContent.isEmpty()) {System.out.println("[加密装饰器] 读取到加密内容, 正在解密。");return decrypt(encryptedContent);}return "";}
}public class CompressionDecorator extends FileOperationDecorator {public CompressionDecorator(FileOperation decoratedFileOperation) {super(decoratedFileOperation);}private byte[] compress(String data) throws IOException {System.out.println("[压缩装饰器] 正在压缩数据...");byte[] input = data.getBytes(StandardCharsets.UTF_8);Deflater deflater = new Deflater();deflater.setInput(input);deflater.finish();ByteArrayOutputStream outputStream = new ByteArrayOutputStream(input.length);byte[] buffer = new byte[1024];while (!deflater.finished()) {int count = deflater.deflate(buffer);outputStream.write(buffer, 0, count);}outputStream.close();return outputStream.toByteArray();}private String decompress(byte[] compressedData) throws IOException, DataFormatException {System.out.println("[压缩装饰器] 正在解压缩数据...");Inflater inflater = new Inflater();inflater.setInput(compressedData);ByteArrayOutputStream outputStream = new ByteArrayOutputStream(compressedData.length);byte[] buffer = new byte[1024];while (!inflater.finished()) {int count = inflater.inflate(buffer);outputStream.write(buffer, 0, count);}outputStream.close();return new String(outputStream.toByteArray(), StandardCharsets.UTF_8);}@Overridepublic void write(String filePath, String content) {try {byte[] compressedContent = this.compress(content);String encodedCompressedContent = Base64.getEncoder().encodeToString(compressedContent);System.out.println("[压缩装饰器] 写入压缩并编码后的内容。");decoratedFileOperation.write(filePath, encodedCompressedContent);} catch (IOException e) {System.out.println("压缩写入失败: " + e.getMessage());}}@Overridepublic String read(String filePath) {String encodedCompressedContent = decoratedFileOperation.read(filePath);if (encodedCompressedContent != null && !encodedCompressedContent.isEmpty()) {try {// 将 Base64 字符串解码为字节数组byte[] compressedContent = Base64.getDecoder().decode(encodedCompressedContent);System.out.println("[压缩装饰器] 读取到压缩内容, 正在解压缩。");return this.decompress(compressedContent);} catch (IOException | DataFormatException e) {System.out.println("解压缩读取失败: " + e.getMessage());}}return "";}}
  • EncryptionDecorator 具体的加密装饰器, 继承 FileOperationDecorator, 在 write 方法中对内容进行加密后再写入, 在 read 方法中读取加密内容后再解密。

  • CompressionDecorator 具体的压缩装饰器, 继承 FileOperationDecorator, 在 write 方法中对内容进行压缩后再写入, 在 read 方法中读取压缩内容后再解压缩。

测试类
public class ProxyDecoratorText {@Testpublic void test_proxyAndDecorator() {System.out.println("\n--- 场景1: 普通用户写入普通文件 ---");FileOperation normalUserFileOp = new FileAccessProxy("user1");normalUserFileOp.write("normal_file.txt", "这是一段普通文本内容。");String readContent1 = normalUserFileOp.read("normal_file.txt");System.out.println("最终读取内容: " + readContent1);System.out.println("\n--- 场景2: 普通用户尝试写入敏感文件 ---");normalUserFileOp.write("sensitive_data.txt", "这是敏感数据。");System.out.println("\n--- 场景3: 管理员写入加密的敏感文件 ---");FileOperation adminFileOp = new FileAccessProxy("admin");EncryptionDecorator encryptionAdminFileOp = new EncryptionDecorator(adminFileOp);encryptionAdminFileOp.write("sensitive_encrypted_file.txt", "这是一段需要加密的敏感信息。");String readContent2 = encryptionAdminFileOp.read("sensitive_encrypted_file.txt");System.out.println("最终读取内容: " + readContent2);System.out.println("\n--- 场景4: 管理员写入压缩并加密的大文件 ---");String largeContent = "这是一段非常非常长的大文本内容,需要进行压缩和加密处理,以节省存储空间并保证数据安全。";CompressionDecorator compressionEncryptedAdminFileOp = new CompressionDecorator(new EncryptionDecorator(adminFileOp));compressionEncryptedAdminFileOp.write("large_compressed_encrypted_file.txt", largeContent);String readContent3 = compressionEncryptedAdminFileOp.read("large_compressed_encrypted_file.txt");System.out.println("最终读取内容: " + readContent3);}}
运行结果
--- 场景1: 普通用户写入普通文件 ---
[文件访问代理] 权限检查通过: 用户 user1 可以执行 write 操作。[真实文件操作] 正在将内容写入文件: normal_file.txt
写入成功, 文件内容为: 这是一段普通文本内容。
[文件访问代理] 权限检查通过: 用户 user1 可以执行 read 操作。[真实文件操作] 正在从文件读取内容: normal_file.txt
读取成功, 文件内容为: 这是一段普通文本内容。
最终读取内容: 这是一段普通文本内容。--- 场景2: 普通用户尝试写入敏感文件 ---
[文件访问代理] 权限不足: 用户 user1 无权写入敏感文件: sensitive_data.txt--- 场景3: 管理员写入加密的敏感文件 ---
[加密装饰器] 正在加密数据...
[加密装饰器] 加入加密后的内容。
[文件访问代理] 权限检查通过: 用户 admin 可以执行 write 操作。[真实文件操作] 正在将内容写入文件: sensitive_encrypted_file.txt
写入成功, 文件内容为: 6L+Z5piv5LiA5q616ZyA6KaB5Yqg5a+G55qE5pWP5oSf5L+h5oGv44CC
[文件访问代理] 权限检查通过: 用户 admin 可以执行 read 操作。[真实文件操作] 正在从文件读取内容: sensitive_encrypted_file.txt
读取成功, 文件内容为: 6L+Z5piv5LiA5q616ZyA6KaB5Yqg5a+G55qE5pWP5oSf5L+h5oGv44CC
[加密装饰器] 读取到加密内容, 正在解密。
[加密装饰器] 正在解密数据...
最终读取内容: 这是一段需要加密的敏感信息。--- 场景4: 管理员写入压缩并加密的大文件 ---
[压缩装饰器] 正在压缩数据...
[压缩装饰器] 写入压缩并编码后的内容。
[加密装饰器] 正在加密数据...
[加密装饰器] 加入加密后的内容。
[文件访问代理] 权限检查通过: 用户 admin 可以执行 write 操作。[真实文件操作] 正在将内容写入文件: large_compressed_encrypted_file.txt
写入成功, 文件内容为: ZUp3dHlzOE93VEFBQitCWGI1ZXRZa3BFTW9aT1NJWXRtZFlCNlZqU2g5RmYvNXk4QWdlWDcvUUZzM2Fsc3BvNGVZL1ZEbHIvTFl6ZnBLalBiamx5b2dQTElQdlB3S01nNFVTRDJZWUR4MnppaHhZTGpud1B4VkNuZnM1K3g3Nk9JVSs4b0xpVVNCcmZQdVBxaHY1aFRSVVVkY1hWVFNYa0dGbnpKc2tYa2JCYit3PT0=
[文件访问代理] 权限检查通过: 用户 admin 可以执行 read 操作。[真实文件操作] 正在从文件读取内容: large_compressed_encrypted_file.txt
读取成功, 文件内容为: ZUp3dHlzOE93VEFBQitCWGI1ZXRZa3BFTW9aT1NJWXRtZFlCNlZqU2g5RmYvNXk4QWdlWDcvUUZzM2Fsc3BvNGVZL1ZEbHIvTFl6ZnBLalBiamx5b2dQTElQdlB3S01nNFVTRDJZWUR4MnppaHhZTGpud1B4VkNuZnM1K3g3Nk9JVSs4b0xpVVNCcmZQdVBxaHY1aFRSVVVkY1hWVFNYa0dGbnpKc2tYa2JCYit3PT0=
[加密装饰器] 读取到加密内容, 正在解密。
[加密装饰器] 正在解密数据...
[压缩装饰器] 读取到压缩内容, 正在解压缩。
[压缩装饰器] 正在解压缩数据...
最终读取内容: 这是一段非常非常长的大文本内容,需要进行压缩和加密处理,以节省存储空间并保证数据安全。Process finished with exit code 0

组合优势

  1. 代理模式负责基础的访问控制, 而装饰器模式负责功能增强, 使得每个类的职责更加单一。

  2. 装饰器模式允许在运行时动态地组合多种功能, 代理模式作为功能链的起点, 确保所有操作都经过必要的访问控制, 为后续的功能增强提供了安全的基础。

代理模式+策略模式

策略模式

行为型设计模式, 定义一系列算法, 将每个算法封装并使它们可以互相替换。

案例

在电商支付系统中, 用户可以选择多种支付方式(如信用卡支付、支付宝支付、微信支付), 每种支付方式的实现细节不同, 但它们都提供了一个统一的 支付接口, 同时, 为确保系统的安全性和合规性, 所有支付操作都需要进行严格的安全审计。

模式职责

  • 代理模式: 作为支付操作的统一入口, 负责在实际支付操作执行前后进行安全审计、日志记录、身份验证等非核心业务逻辑, 确保了所有支付请求都经过必要的安全检查。

  • 策略模式: 封装不同的支付算法, 使得这些支付方式可以相互替换, 上下文根据用户的选择, 动态地使用不同的支付策略来完成支付。

代码示例

抽象策略
public interface PaymentStrategy {void pay(double amount);}
  • 定义了具体支付方式必须实现的 pay(double amount) 方法。

具体策略
public class CreditCardPaymentStrategy implements PaymentStrategy {private String cardNumber;private String expiryDate;public CreditCardPaymentStrategy(String cardNumber, String expiryDate) {this.cardNumber = cardNumber;this.expiryDate = expiryDate;}@Overridepublic void pay(double amount) {System.out.println("[信用卡支付策略] 正在使用信用卡 " + cardNumber + " (有效期: " + expiryDate + ") 支付" + amount + " 元。");System.out.println("[信用卡支付策略] 支付成功。");}}public class AlipayPaymentStrategy implements PaymentStrategy {private String userId;public AlipayPaymentStrategy(String userId) {this.userId = userId;}@Overridepublic void pay(double amount) {System.out.println("[支付宝支付策略] 正在使用支付宝账户 " + userId + " 支付 " + amount + " 元。");System.out.println("[支付宝支付策略] 支付成功。");}
}public class WechatPaymentStrategy implements PaymentStrategy {private String openId;public WechatPaymentStrategy(String openId) {this.openId = openId;}@Overridepublic void pay(double amount) {System.out.println("[微信支付策略] 正在使用微信账户 " + openId + " 支付 " + amount + " 元。");System.out.println("[微信支付策略] 支付成功。");}
}
  • CreditCardPaymentStrategyAlipayPaymentStrategyWechatPaymentStrategy 分别实现了 PaymentStrategy 接口, 并封装了各自支付方式的详细逻辑, 具体策略类之间可以相互替换。

抽象主题/策略上下文接口
public interface PaymentService {void processPayment(double amount, PaymentStrategy strategy);}
  • 定义了 processPayment(double amount, PaymentStrategy strategy) 方法, 接受支付金额和 PaymentStrategy 作为参数。

真实主题
public class RealPaymentService implements PaymentService {@Overridepublic void processPayment(double amount, PaymentStrategy strategy) {System.out.println("\n[真实支付服务] 开始处理支付请求, 金额: " + amount + " 元。");if (strategy == null) {System.out.println("[真实支付服务] 错误: 未指定支付策略。");return;}strategy.pay(amount);System.out.println("[真实支付服务] 支付请求处理完毕。");}
}
  • 持有一个 PaymentStrategy 的引用, 并在 processPayment 方法中调用该策略的 pay 方法来完成实际支付。

代理主题
public class SecurityAuditPaymentProxy implements PaymentService {private RealPaymentService realPaymentService;private String currentUserId;public SecurityAuditPaymentProxy(RealPaymentService realPaymentService, String currentUserId) {this.realPaymentService = realPaymentService;this.currentUserId = currentUserId;}private boolean preAudit(double amount, PaymentStrategy strategy) {System.out.println("\n[安全审计代理] 前置审计: 用户 " + currentUserId + " 尝试支付 " + amount + " 元, 使用策略: " + strategy.getClass().getSimpleName() + " 在 " + new Date());if ("blockedUser".equals(currentUserId)) {System.out.println("[安全审计代理] 审计失败: 用户 " + currentUserId + "被阻止支付。");return false;}System.out.println("[安全审计代理] 前置审计通过。");return true;}private void postAudit(double amount, PaymentStrategy strategy) {System.out.println("[安全审计代理] 后置审计: 用户 " + currentUserId + " 支付 " + amount + " 元操作完成, 使用策略: " + strategy.getClass().getSimpleName() + "..");}@Overridepublic void processPayment(double amount, PaymentStrategy strategy) {if (this.preAudit(amount, strategy)) {realPaymentService.processPayment(amount, strategy);this.postAudit(amount, strategy);}else {System.out.println("[安全审计代理] 支付请求被拒绝。");}}
}
  • 实现了 PaymentService 接口, 并持有一个 RealPaymentService 的引用。在 processPayment 方法中, 在调用 realPaymentService.processPayment 之前 执行 preAudit(前置审计), 在之后执行 postAudit

测试类
public class ProxyStrategyTest {@Testpublic void test_proxyStrategy() {System.out.println("\n--- 用户1(正常用户)使用信用卡支付 ---");PaymentService user1PaymentService = new SecurityAuditPaymentProxy(new RealPaymentService(), "user_A");PaymentStrategy creditCardStrategy = new CreditCardPaymentStrategy("1234-5678-9012", "12/25");user1PaymentService.processPayment(100.00, creditCardStrategy);System.out.println("\n--- 用户2(正常用户)使用支付宝支付 ---");PaymentService user2PaymentService = new SecurityAuditPaymentProxy(new RealPaymentService(), "user_B");PaymentStrategy alipayStrategy = new AlipayPaymentStrategy("alipay_user_B");user2PaymentService.processPayment(50.50, alipayStrategy);System.out.println("\n--- 用户3(被阻止用户)尝试使用微信支付 ---");PaymentService user3PaymentService = new SecurityAuditPaymentProxy(new RealPaymentService(), "blockedUser");PaymentStrategy wechatStrategy = new WechatPaymentStrategy("wechat_open_id_C");user3PaymentService.processPayment(200.00, wechatStrategy);System.out.println("\n--- 用户4(正常用户)再次使用支付宝支付 ---");PaymentService user4PaymentService = new SecurityAuditPaymentProxy(new RealPaymentService(), "user_D");PaymentStrategy anotherAlipayStrategy = new AlipayPaymentStrategy("alipay_user_D");user4PaymentService.processPayment(75.00, anotherAlipayStrategy);}}
执行结果
--- 用户1(正常用户)使用信用卡支付 ---[安全审计代理] 前置审计: 用户 user_A 尝试支付 100.0 元, 使用策略: CreditCardPaymentStrategy 在 Mon Sep 29 10:28:28 CST 2025
[安全审计代理] 前置审计通过。[真实支付服务] 开始处理支付请求, 金额: 100.0 元。
[信用卡支付策略] 正在使用信用卡 1234-5678-9012 (有效期: 12/25) 支付100.0 元。
[信用卡支付策略] 支付成功。
[真实支付服务] 支付请求处理完毕。
[安全审计代理] 后置审计: 用户 user_A 支付 100.0 元操作完成, 使用策略: CreditCardPaymentStrategy..--- 用户2(正常用户)使用支付宝支付 ---[安全审计代理] 前置审计: 用户 user_B 尝试支付 50.5 元, 使用策略: AlipayPaymentStrategy 在 Mon Sep 29 10:28:28 CST 2025
[安全审计代理] 前置审计通过。[真实支付服务] 开始处理支付请求, 金额: 50.5 元。
[支付宝支付策略] 正在使用支付宝账户 alipay_user_B 支付 50.5 元。
[支付宝支付策略] 支付成功。
[真实支付服务] 支付请求处理完毕。
[安全审计代理] 后置审计: 用户 user_B 支付 50.5 元操作完成, 使用策略: AlipayPaymentStrategy..--- 用户3(被阻止用户)尝试使用微信支付 ---[安全审计代理] 前置审计: 用户 blockedUser 尝试支付 200.0 元, 使用策略: WechatPaymentStrategy 在 Mon Sep 29 10:28:28 CST 2025
[安全审计代理] 审计失败: 用户 blockedUser被阻止支付。
[安全审计代理] 支付请求被拒绝。--- 用户4(正常用户)再次使用支付宝支付 ---[安全审计代理] 前置审计: 用户 user_D 尝试支付 75.0 元, 使用策略: AlipayPaymentStrategy 在 Mon Sep 29 10:28:28 CST 2025
[安全审计代理] 前置审计通过。[真实支付服务] 开始处理支付请求, 金额: 75.0 元。
[支付宝支付策略] 正在使用支付宝账户 alipay_user_D 支付 75.0 元。
[支付宝支付策略] 支付成功。
[真实支付服务] 支付请求处理完毕。
[安全审计代理] 后置审计: 用户 user_D 支付 75.0 元操作完成, 使用策略: AlipayPaymentStrategy..Process finished with exit code 0

组合优势

  1. 当需要增加新的支付方式时, 只需添加新的具体策略类; 当安全审计需求变化时, 只需修改代理类。

  2. 代理模式专注于安全审计和访问控制, 而策略模式专注于封装不同的支付算法, 两者职责明确, 互不干扰。

http://www.dtcms.com/a/423389.html

相关文章:

  • 招聘网站的SEO怎么做建站免费软件
  • 内嵌式模组在物联网设备中如何体现?
  • 【Vue实现跳转页面】功能 - 总结
  • 网站刷流量对网站有影响吗猪八戒网做网站如何付款
  • Linux网络编程05:IO多路转接(万字图文解析)
  • 在 Kubernetes 集群中手动部署开发的应用程序
  • 不联网设备如何精确记时的
  • 网站首页添加代码寄生虫seo教程
  • 黄冈网站推广策略黄浦网站建设
  • 在JavaScript / HTML中,浏览器提示 “Refused to execute inline event handler” 错误
  • 怎样做一个公司网站个人网站做商城
  • 模拟面试 - 第6轮
  • PostgreSQL WAL 日志发展史 - pg8
  • 第4篇 vs2019+QT调用SDK连接海康相机显示图片
  • 高通平台WiFi学习--WLAN Offload技术
  • 微信小程序app.js中每30秒调用一次wx.getLocation
  • 遥感云平台-GEE分块下载与拼接
  • 深圳市建设监理协会网站wordpress后台接口数据
  • UNIX下C语言编程与实践10-UNIX 动态库隐式调用:编译链接配置与路径问题排查
  • 网站虚拟主持人制作建设一个网站需要条件
  • 网站建设中文百北京住房和城乡建设局门户网站
  • uni-app 模板语法修复说明
  • OpenLayers地图交互 -- 章节十八:拖拽旋转和缩放交互详解
  • 6DOF-Euler Angles
  • 【代码随想录day 29】 力扣 860.柠檬水找零
  • 医疗智能体的技术演进与路径分析:从多模态大模型到高阶综合智能体
  • 西安制作网站的电话深圳seo网站推广方案
  • 开放、协同,2025 云栖大会“操作系统开源与 AI 进化分论坛”精彩回顾
  • Codeforces Round 1050 A. Sublime Sequence
  • 欧拉路径与欧拉回路