二十三种设计模式(八)--装饰器模式

装饰器模式(DECORATOR)

装饰器模式解决的是基座+配件灵活组合的问题

装饰器模式是包装器模式中的一种, 它生成最终对象的过程也是包装器对象将包装器层层包裹起来.

装饰器模式需要两个关键角色

基础类: Component + ComponentConcrete

若干包装类: Wrapper + WrapperConcrete1 + WrapperConcrete2 + WrapperConcrete3 + ...

在包装类接口中, 通过类成员引用的方式, 将基础类聚合进来, 再通过包装类层层包裹, 简单实现过程如下:

java 复制代码
// 基础类统一接口
// 定义统一行为,保证基础类和装饰器行为一致,是装饰器模式的 "契约"
interface Component {
    void exec();
}
// 基础类实现
class ConcreteComponent implements Component {
    @Override
    public void exec() {
        System.out.println("ConcreteComponent exec");
    }
}


// 装饰器配件抽象类
abstract class Decorator implements Component {
    private Component wrap_c; // 这里是基础类的引用, 实现包装类的关键
    Decorator(Component c) {
        this.wrap_c = c; // 构造时注入基础类, 将在此基础上实现层层包裹
    }

    @Override
    public void exec() {
        System.out.println("Decorator exec");
        wrap_c.exec();  // 这一步很关键, 基础类对象的exec方法是初始化完成之后链式调用的第一步
    }
}

// 配件1
class D1 extends Decorator {
    D1(Component c) {
        super(c);
    }

    @Override
    public void exec() {
        // 一般先执行自己的增强逻辑,再调用被装饰对象的方法
        System.out.println("D1");
        super.exec();
    }
}
// 配件2
class D2 extends Decorator {
    D2(Component c) {
        super(c);
    }

    @Override
    public void exec() {
        System.out.println("D2");
        super.exec();
    }
}
// 配件3
class D3 extends Decorator {
    D3(Component c) {
        super(c);
    }

    @Override
    public void exec() {
        System.out.println("D3");
        super.exec();
    }
}

实例化多层对象包裹

java 复制代码
public class DecoratorPattern {
    public static void main(String[] args) {
        // 创建基座实例
        Component c = new ConcreteComponent();
        // 在基座上包裹配件D1, D2, D3
        D3 d3 = new D3(new D2(new D1(c)));
        d3.exec();

        System.out.println("----------- separate line -----------");
        // 动态装配, 先实例化基座
        Component cc = new ConcreteComponent();
        // 随机增加D1, D3两个配件
        Component dd1 = new D1(cc);
        Component dd3 = new D3(dd1);
        dd3.exec();
    }
}

运行结果:

复制代码
D3
Decorator exec
D2
Decorator exec
D1
Decorator exec
ConcreteComponent exec
----------- separate line -----------
D3
Decorator exec
D1
Decorator exec
ConcreteComponent exec

要点:
1. 每个装饰器都只增强,不改变原功能
2. 保持与原接口相同,确保可层层包裹

在实际开发过程中的装饰器模式示例如下

基础类统一接口及基础类实现

java 复制代码
// 支付服务接口(抽象组件):定义核心支付功能
interface PaymentService {
    /**
     * 发起支付
     * @param userId 用户ID
     * @param amount 支付金额(分)
     * @param orderId 订单ID
     * @return 支付结果(成功/失败)
     */
    boolean pay(String userId, long amount, String orderId);
}

// 支付宝支付实现, 基础类
class AlipayPayment implements PaymentService {
    @Override
    public boolean pay(String userId, long amount, String orderId) {
        // 模拟支付宝支付核心逻辑(调用第三方API、扣减余额等)
        System.out.printf(
                "[支付宝支付] 发起支付 - 用户ID:%s,订单ID:%s,金额:%d分%n",
                userId, orderId, amount
        );
        // 模拟支付成功(实际场景需处理异常和返回结果)
        return true;
    }
}

定义装饰器抽象类, 装饰器模式的关键

java 复制代码
// 装饰器抽象类, 聚合基础类引用
abstract class PaymentDecorator implements PaymentService {
    // 持有被装饰的核心组件
    protected final PaymentService paymentService;

    // 构造器注入,确保被装饰对象非空(企业级代码空校验)
    public PaymentDecorator(PaymentService paymentService) {
        this.paymentService = Objects.requireNonNull(paymentService, "支付服务不能为空");
    }

    // 子类需实现pay方法,添加装饰功能
    @Override
    public abstract boolean pay(String userId, long amount, String orderId);
}

3种装饰器配件具体实现

java 复制代码
// 装饰器配件1, 日志
class LoggingPaymentDecorator extends PaymentDecorator {
    public LoggingPaymentDecorator(PaymentService paymentService) {
        super(paymentService);
    }

    @Override
    public boolean pay(String userId, long amount, String orderId) {
        // 装饰逻辑:支付前记录请求日志
        String requestLog = String.format(
                "[支付日志] 时间:%s,操作:发起支付,用户ID:%s,订单ID:%s,金额:%d分",
                LocalDateTime.now(), userId, orderId, amount
        );
        System.out.println(requestLog);

        // 调用核心支付功能(被装饰对象的方法)
        boolean payResult = paymentService.pay(userId, amount, orderId);

        // 装饰逻辑:支付后记录结果日志
        String resultLog = String.format(
                "[支付日志] 时间:%s,操作:支付完成,订单ID:%s,结果:%s",
                LocalDateTime.now(), orderId, payResult ? "成功" : "失败"
        );
        System.out.println(resultLog);

        return payResult;
    }
}

// 装饰器配件2: 加密
class EncryptionPaymentDecorator extends PaymentDecorator {
    public EncryptionPaymentDecorator(PaymentService paymentService) {
        super(paymentService);
    }

    @Override
    public boolean pay(String userId, long amount, String orderId) {
        // 装饰逻辑:支付前加密参数(模拟AES加密)
        String encryptedUserId = encrypt(userId);
        String encryptedOrderId = encrypt(orderId);
        System.out.printf(
                "[加密处理] 原始参数 - 用户ID:%s,订单ID:%s → 加密后 - %s,%s%n",
                userId, orderId, encryptedUserId, encryptedOrderId
        );

        // 调用核心支付功能(传入加密后的参数,实际场景需解密后使用)
        boolean payResult = paymentService.pay(encryptedUserId, amount, encryptedOrderId);

        // 装饰逻辑:支付后加密返回结果(可选)
        System.out.printf("[加密处理] 支付结果加密:%s → %s%n", payResult, encrypt(String.valueOf(payResult)));

        return payResult;
    }

    // 模拟加密算法(实际用AES/RSA)
    private String encrypt(String content) {
        return "ENC(" + content + "_" + System.currentTimeMillis() + ")";
    }
}

// 装饰器配件3: 权限校验
class AuthorizationPaymentDecorator extends PaymentDecorator {
    public AuthorizationPaymentDecorator(PaymentService paymentService) {
        super(paymentService);
    }

    @Override
    public boolean pay(String userId, long amount, String orderId) {
        // 装饰逻辑:支付前校验权限
        System.out.printf("[权限校验] 校验用户 %s 是否有权限支付订单 %s%n", userId, orderId);
        if (!hasPaymentPermission(userId)) {
            System.err.printf("[权限校验] 用户 %s 无支付权限,拒绝支付%n", userId);
            return false;
        }
        System.out.printf("[权限校验] 用户 %s 权限通过%n", userId);

        // 调用核心支付功能
        return paymentService.pay(userId, amount, orderId);
    }

    // 模拟权限校验(实际查数据库/缓存)
    private boolean hasPaymentPermission(String userId) {
        // 假设用户ID以"U"开头的有权限
        return userId != null && userId.startsWith("U");
    }
}

调用示例

java 复制代码
import java.time.LocalDateTime;
import java.util.Objects;

public class Decorator {
    public static void main(String[] args) {
        // 1. 创建原始支付组件(核心功能)
        PaymentService rawPayment = new AlipayPayment();

        // 2. 动态添加装饰功能(按需组合,顺序可调整)
        // 需求:支付前先校验权限 → 加密参数 → 记录日志 → 执行支付
        PaymentService decoratedPayment = new AuthorizationPaymentDecorator(
                new EncryptionPaymentDecorator(
                        new LoggingPaymentDecorator(rawPayment)
                )
        );

        // 3. 发起支付(使用装饰后的服务)
        boolean result1 = decoratedPayment.pay("U1001", 9999, "ORDER_20251207_001");
    }
}

运行输出:

复制代码
[权限校验] 校验用户 U1001 是否有权限支付订单 ORDER_20251207_001
[权限校验] 用户 U1001 权限通过
[加密处理] 原始参数 - 用户ID:U1001,订单ID:ORDER_20251207_001 → 加密后 - ENC(U1001_1765096216897),ENC(ORDER_20251207_001_1765096216910)
[支付日志] 时间:2025-12-07T16:30:16.924497200,操作:发起支付,用户ID:ENC(U1001_1765096216897),订单ID:ENC(ORDER_20251207_001_1765096216910),金额:9999分
[支付宝支付] 发起支付 - 用户ID:ENC(U1001_1765096216897),订单ID:ENC(ORDER_20251207_001_1765096216910),金额:9999分
[支付日志] 时间:2025-12-07T16:30:16.925494400,操作:支付完成,订单ID:ENC(ORDER_20251207_001_1765096216910),结果:成功
[加密处理] 支付结果加密:true → ENC(true_1765096216925)
相关推荐
@小白鸽1 小时前
1.2.2结构型设计模式
设计模式
白露与泡影1 小时前
从 JDK 8 到 JDK 18,Java 垃圾回收的十次进化
java·开发语言·测试工具
国科安芯1 小时前
AS32A601型MCU芯片flash模块的擦除和编程
java·linux·前端·单片机·嵌入式硬件·fpga开发·安全性测试
青云交2 小时前
Java 大视界 -- Java 大数据机器学习模型在自然语言处理中的对话系统多轮交互优化与用户体验提升
java·大数据·机器学习·自然语言处理·对话系统·多轮交互
90后小陈老师2 小时前
记录一次Figma订阅被多扣费的教训
java·linux·数据库
计算机毕设指导62 小时前
基于微信小程序的心理咨询预约系统【源码文末联系】
java·spring boot·mysql·微信小程序·小程序·tomcat·maven
YJlio2 小时前
Active Directory 工具学习笔记(10.2):AdExplorer 实战(二)— 对象 / 属性 / 搜索 / 快照
java·笔记·学习
青衫码上行2 小时前
【JavaWeb学习 | 第19篇】Filter过滤器
java·学习·servlet·tomcat
diegoXie2 小时前
【R】正则的惰性和贪婪匹配
java·前端·r语言