模式组合应用-代理模式

写在前面

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


代理模式

定义

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

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

角色

  • 抽象主题: 定义了真实主题和代理主题的共同接口, 使得在任何可以使用真实主题的地方都可以使用代理主题。
  • 真实主题: 定义了代理所代表的真实对象, 它包含了业务逻辑, 是代理模式最终要访问的对象。
  • 代理主题: 持有对真实主题的引用, 并实现与真实主题相同的接口, 代理对象可以在将请求转发给真实主题之前或之后, 执行一些预处理或 后处理的逻辑。

使用时主要思考点

  1. 明确代理目的: 在引入代理模式前, 首先要明确代理的目的是什么, 如控制访问(权限控制、远程代理等)、增强功能(如 日志、缓存、事务), 不同的目的可能导致代理的实现方式和复杂程度不同。
  2. 接口一致性: 代理对象和真实对象必须实现相同的接口。
  3. 性能开销: 引入代理会增加一层间接性, 这可能会带来一定的性能开销, 在对性能要求极高的场景下, 需要仔细评估。
  4. 避免过度设计: 一些简单功能可以通过直接调用实现, 引入代理会使系统变得过于复杂。

JDK 动态代理

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

核心接口

  • InvocationHandler 接口: 代理实例的调用处理程序实现的接口。每个代理实例都具有一个关联的调用处理程序。当代理实例上调用方法时, 方法调用将被编码并分派到其调用处理程序的 invoke 方法。
  • Proxy类: 提供了创建动态代理类和实例的静态方法。

代码示例

抽象主题
arduino 复制代码
public interface UserService {

    void registerUser(String username, String password);

    void login(String username, String password);

}
真实主题
csharp 复制代码
public class UserServiceImpl implements UserService {

    @Override
    public 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("用户注册业务逻辑执行完毕。");

    }

    @Override
    public 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("用户登录业务逻辑执行完毕。");
    }

}
动态代理处理器
typescript 复制代码
public class PerformanceLogInvocationHandler implements InvocationHandler {

    private Object target;

    public PerformanceLogInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public 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;

    }
}
测试类
csharp 复制代码
public class JdkDynamicProxyTest {

    @Test
    public 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");

    }

}
运行结果
lua 复制代码
--- 使用 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) 会使对象再次触发连接器。

代码示例

真实主题
vbnet 复制代码
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 动态代理拦截器
vbnet 复制代码
public class CacheAndLogMethodInterceptor implements MethodInterceptor {

    private Object target;

    private Map<String, Object> cache = new HashMap<>();

    public CacheAndLogMethodInterceptor(Object target) {
        this.target = target;
    }

    @Override
    public 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;
    }

}
测试类
csharp 复制代码
public class CglibDynamicProxyTest {

    @Test
    public 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");

    }

}
运行结果
lua 复制代码
--- 使用 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_A

Process finished with exit code 0

代理模式+装饰器模式

装饰器模式

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

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

案例

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

  1. 权限控制: 只有特定用户才能访问某些文件。
  2. 数据加密: 敏感文件在写入时需要加密, 读取时需要解密。
  3. 数据压缩: 大文件在传输或存储时需要压缩, 读取时需要解压。

模式职责

  • 代理模式: 作为文件操作的入口, 提供基础的访问控制或预处理, 例如检查文件是否存在、用户是否有权限进行操作等, 确保了文件操作的合法性。
  • 装饰器模式: 动态地添加额外的功能, 如加密、解密、压缩、解压缩等。

代码示例

抽象组件/抽象主题
arduino 复制代码
public interface FileOperation {

    void write(String filePath, String content);

    String read(String filePath);

}
  • 定义了文件读写操作的抽象, 是代理和装饰器模式共同的接口, 确保了它们可以相互兼容和组合。
具体组件/真实主题
typescript 复制代码
public class RealFileOperation implements FileOperation {

    private String fileContent = "";

    @Override
    public void write(String filePath, String content) {

        System.out.println("\n[真实文件操作] 正在将内容写入文件: " + filePath);
        this.fileContent = content;
        System.out.println("写入成功, 文件内容为: " + content);

    }

    @Override
    public String read(String filePath) {

        System.out.println("\n[真实文件操作] 正在从文件读取内容: " + filePath);
        System.out.println("读取成功, 文件内容为: " + fileContent);

        return fileContent;
    }
}
  • 实现了 FileOperation 接口, 负责模拟实际的文件读写操作, 不包含任何权限、加密或压缩逻辑。
代理主题
typescript 复制代码
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;
    }

    @Override
    public void write(String filePath, String content) {
        if (this.checkPermission(filePath, "write")) {
            realFileOperation.write(filePath, content);
        }
    }

    @Override
    public String read(String filePath) {
        if (this.checkPermission(filePath, "read")) {
            return realFileOperation.read(filePath);
        }

        return "";
    }
}
  • 实现了 FileOperation 接口, 并持有一个 RealFileOperation 实例。主要职责是在调用真实文件操作之前, 进行用户权限的检查。如果权限不足则阻止操作;否则,将请求转发给真实文件操作类。
抽象装饰器类
typescript 复制代码
public abstract class FileOperationDecorator implements FileOperation {

    protected FileOperation decoratedFileOperation;

    public FileOperationDecorator(FileOperation decoratedFileOperation) {
        this.decoratedFileOperation = decoratedFileOperation;
    }

    @Override
    public void write(String filePath, String content) {
        decoratedFileOperation.write(filePath, content);
    }

    @Override
    public String read(String filePath) {
        return decoratedFileOperation.read(filePath);
    }

}
  • 实现了 FileOperation 接口, 并包含一个 FileOperation 类型的成员变量, 用于引用被装饰的对象, 默认将所有方法调用转发给被装饰的对象。
具体装饰器类
java 复制代码
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);
    }

    @Override
    public void write(String filePath, String content) {
        String encryptedContent = this.encrypt(content);
        System.out.println("[加密装饰器] 加入加密后的内容。");
        decoratedFileOperation.write(filePath, encryptedContent);
    }

    @Override
    public 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);
    }

    @Override
    public 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());
        }

    }

    @Override
    public 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 方法中读取压缩内容后再解压缩。
测试类
ini 复制代码
public class ProxyDecoratorText {

    @Test
    public 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);

    }

}
运行结果
ini 复制代码
--- 场景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. 装饰器模式允许在运行时动态地组合多种功能, 代理模式作为功能链的起点, 确保所有操作都经过必要的访问控制, 为后续的功能增强提供了安全的基础。

代理模式+策略模式

策略模式

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

案例

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

模式职责

  • 代理模式: 作为支付操作的统一入口, 负责在实际支付操作执行前后进行安全审计、日志记录、身份验证等非核心业务逻辑, 确保了所有支付请求都经过必要的安全检查。
  • 策略模式: 封装不同的支付算法, 使得这些支付方式可以相互替换, 上下文根据用户的选择, 动态地使用不同的支付策略来完成支付。

代码示例

抽象策略
csharp 复制代码
public interface PaymentStrategy {

    void pay(double amount);

}
  • 定义了具体支付方式必须实现的 pay(double amount) 方法。
具体策略
typescript 复制代码
public class CreditCardPaymentStrategy implements PaymentStrategy {

    private String cardNumber;

    private String expiryDate;

    public CreditCardPaymentStrategy(String cardNumber, String expiryDate) {
        this.cardNumber = cardNumber;
        this.expiryDate = expiryDate;
    }

    @Override
    public 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;
    }

    @Override
    public 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;
    }

    @Override
    public void pay(double amount) {
        System.out.println("[微信支付策略] 正在使用微信账户 " + openId + " 支付 " + amount + " 元。");
        System.out.println("[微信支付策略] 支付成功。");
    }
}
  • CreditCardPaymentStrategyAlipayPaymentStrategyWechatPaymentStrategy 分别实现了 PaymentStrategy 接口, 并封装了各自支付方式的详细逻辑, 具体策略类之间可以相互替换。
抽象主题/策略上下文接口
csharp 复制代码
public interface PaymentService {

    void processPayment(double amount, PaymentStrategy strategy);

}
  • 定义了 processPayment(double amount, PaymentStrategy strategy) 方法, 接受支付金额和 PaymentStrategy 作为参数。
真实主题
csharp 复制代码
public class RealPaymentService implements PaymentService {

    @Override
    public 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 方法来完成实际支付。
代理主题
csharp 复制代码
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() + "..");
    }

    @Override
    public 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
测试类
ini 复制代码
public class ProxyStrategyTest {

    @Test
    public 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);
    }

}
执行结果
less 复制代码
--- 用户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. 代理模式专注于安全审计和访问控制, 而策略模式专注于封装不同的支付算法, 两者职责明确, 互不干扰。
相关推荐
乐予吕2 小时前
用 HTTP OPTIONS 发现 API 的隐藏能力
后端·http·api
盛华科技2 小时前
QT实战课程_监控系统
后端
用户203735549812 小时前
黑马博学谷-Java并发编程原理精讲
后端
Java水解2 小时前
MySQL 表约束实战指南:从概念到落地,守护数据完整性
后端·mysql
盛华科技3 小时前
YOLOv5怎么做改进?大佬手把手带你在YOLOv5中添加4种注意力机制,训练自己的数据集!
后端
程序员蜗牛4 小时前
微信登录之OpenID与UnionID获取全流程解析
后端
SimonKing4 小时前
SpringBoot多模板引擎整合难题?一篇搞定JSP、Freemarker与Thymeleaf!
java·后端·程序员
rannn_1114 小时前
【LeetCode hot100|Week4】链表
后端·算法·leetcode·链表
SYC_MORE4 小时前
多线程环境下处理Flask上下文问题的文档
后端·python·flask