Java设计模式应用--装饰器模式

一、概念

装饰器模式概念:是指允许对一个现有的对象加入其它额外的功能并且不改变其原来的结构,属于结构型模式。这种模式通常会创建一个装饰类来包装原有的类以达到装饰的效果。

装饰器模式,顾名思义,就是对已经存在的某些类进行装饰,以此来扩展一些功能。

意图​:动态地给一个对象添加额外的职责。就增加功能来说,装饰模式相比生成子类更为灵活。

二、应用

装饰器模式在实际项目中的应用:以我从事的支付行业为例。

核心角色​:

​Component(抽象组件)​​:通常是接口或抽象类,定义了核心对象的行为。在支付系统中,这就是最核心的"支付"操作。

​ConcreteComponent(具体组件)​ ​:实现了Component接口的核心对象,是我们要动态添加功能的对象。例如,一个基础的、只完成最基本支付流程的实现。

​Decorator(抽象装饰器)​ ​:持有一个Component对象的引用,并实现Component接口。它是所有具体装饰器的父类。

ConcreteDecorator(具体装饰器)​​:向组件添加具体职责的类。

在支付系统中的常用应用场景

在支付中,支付流程是主流程,可以用装饰器模式给主流程进行扩展。

示例1:如一个简单的示例:日志记录,验证逻辑。当然在整个支付中,这样的应用很少,只是做个简单的引用。

简化代码:

java 复制代码
// 1. 抽象组件 (Component)
public interface PaymentService {
    PayResult pay(PaymentRequest request);
}
java 复制代码
// 2. 具体组件 (ConcreteComponent) - 基础支付实现
public class BasicPaymentService implements PaymentService {
    @Override
    public PayResult pay(PaymentRequest request) {
        // 模拟调用支付网关的核心逻辑
        System.out.println("调用支付网关,扣款 " + request.getAmount() + " 元...");
        // ... 实际网络调用
        return new PayResult(200, "支付成功", "T123456");
    }
}
java 复制代码
// 3. 抽象装饰器 (Decorator)
public abstract class PaymentDecorator implements PaymentService {
    protected PaymentService wrapped;

    public PaymentDecorator(PaymentService wrapped) {
        this.wrapped = wrapped;
    }

    @Override
    public PayResult pay(PaymentRequest request) {
        return wrapped.pay(request);
    }
}
java 复制代码
// 4. 具体装饰器 (ConcreteDecorator) - 日志记录
public class LoggingPaymentDecorator extends PaymentDecorator {
    public LoggingPaymentDecorator(PaymentService wrapped) {
        super(wrapped);
    }

    @Override
    public PayResult pay(PaymentRequest request) {
        System.out.println("[INFO] 开始支付请求: " + request);
        try {
            PayResult result = super.pay(request);
            System.out.println("[INFO] 支付请求完成: " + result);
            return result;
        } catch (Exception e) {
            System.out.println("[ERROR] 支付请求异常: " + e.getMessage());
            throw e;
        }
    }
}
java 复制代码
// 5. 具体装饰器 (ConcreteDecorator) - 参数验证
public class ValidationPaymentDecorator extends PaymentDecorator {
    public ValidationPaymentDecorator(PaymentService wrapped) {
        super(wrapped);
    }

    @Override
    public PayResult pay(PaymentRequest request) {
        // 简单的验证逻辑
        if (request.getAmount() == null || request.getAmount().compareTo(BigDecimal.ZERO) <= 0) {
            throw new IllegalArgumentException("支付金额必须大于0");
        }
        if (request.getToUserId() == null) {
            throw new IllegalArgumentException("收款方不能为空");
        }
        System.out.println("[VALID] 参数验证通过");
        return super.pay(request);
    }
}
java 复制代码
// 测试类
public class PaymentDemo {
    public static void main(String[] args) {
        // 构建一个被"验证"和"日志"装饰的基础支付服务
        PaymentService paymentService = new BasicPaymentService();
        paymentService = new ValidationPaymentDecorator(paymentService);
        paymentService = new LoggingPaymentDecorator(paymentService);

        // 创建支付请求
        PaymentRequest request = new PaymentRequest(new BigDecimal("100.00"), "userA", "userB");

        // 执行支付
        PayResult result = paymentService.pay(request);
    }
}
java 复制代码
输出结果:
[INFO] 开始支付请求: PaymentRequest(amount=100.00, fromUserId=userA, toUserId=userB)
[VALID] 参数验证通过
调用支付网关,扣款 100.00 元...
[INFO] 支付请求完成: PayResult(success=true, msg=支付成功, transactionId=T123456)

实际上在支付领域中,对于支付渠道路由/组合一般会采用装饰器和责任链设计模式。还有营销活动的叠加的技术选型中采用装饰器/策略模式。

示例2:如:电商系统中常见的优惠叠加装饰器

java 复制代码
// 支付金额计算接口
public interface AmountCalculator {
    CalculationResult calculate(Order order);
}
java 复制代码
// 基础计算器 - 原价计算
@Component
public class BaseAmountCalculator implements AmountCalculator {
    @Override
    public CalculationResult calculate(Order order) {
        BigDecimal originalAmount = order.getTotalAmount();
        return new CalculationResult(originalAmount, originalAmount, "原价计算");
    }
}
java 复制代码
// 优惠券装饰器
@Component
public class CouponCalculatorDecorator implements AmountCalculator {
    private final AmountCalculator wrapped;
    private final CouponService couponService;
    
    public CouponCalculatorDecorator(AmountCalculator wrapped, CouponService couponService) {
        this.wrapped = wrapped;
        this.couponService = couponService;
    }
    
    @Override
    public CalculationResult calculate(Order order) {
        // 先计算基础金额
        CalculationResult baseResult = wrapped.calculate(order);
        
        // 应用优惠券
        Coupon coupon = couponService.getApplicableCoupon(order);
        if (coupon != null) {
            BigDecimal discountAmount = coupon.calculateDiscount(baseResult.getFinalAmount());
            BigDecimal finalAmount = baseResult.getFinalAmount().subtract(discountAmount);
            
            return new CalculationResult(
                baseResult.getOriginalAmount(),
                finalAmount,
                baseResult.getDescription() + ", 优惠券减免: " + discountAmount
            );
        }
        
        return baseResult;
    }
}
java 复制代码
// 积分抵扣装饰器
@Component
public class PointsCalculatorDecorator implements AmountCalculator {
    private final AmountCalculator wrapped;
    private final PointsService pointsService;
    
    public PointsCalculatorDecorator(AmountCalculator wrapped, PointsService pointsService) {
        this.wrapped = wrapped;
        this.pointsService = pointsService;
    }
    
    @Override
    public CalculationResult calculate(Order order) {
        CalculationResult baseResult = wrapped.calculate(order);
        
        // 计算可用积分抵扣
        int availablePoints = pointsService.getAvailablePoints(order.getUserId());
        BigDecimal pointsDiscount = pointsService.calculatePointsValue(availablePoints);
        
        if (pointsDiscount.compareTo(BigDecimal.ZERO) > 0) {
            BigDecimal finalAmount = baseResult.getFinalAmount().subtract(pointsDiscount);
            
            return new CalculationResult(
                baseResult.getOriginalAmount(),
                finalAmount.max(BigDecimal.ZERO), // 确保不低于0
                baseResult.getDescription() + ", 积分抵扣: " + pointsDiscount
            );
        }
        
        return baseResult;
    }
}
java 复制代码
// 会员折扣装饰器
@Component
public class MemberDiscountCalculatorDecorator implements AmountCalculator {
    private final AmountCalculator wrapped;
    private final MemberService memberService;
    
    public MemberDiscountCalculatorDecorator(AmountCalculator wrapped, MemberService memberService) {
        this.wrapped = wrapped;
        this.memberService = memberService;
    }
    
    @Override
    public CalculationResult calculate(Order order) {
        CalculationResult baseResult = wrapped.calculate(order);
        
        // 获取会员折扣
        BigDecimal discountRate = memberService.getMemberDiscount(order.getUserId());
        if (discountRate.compareTo(BigDecimal.ONE) < 0) {
            BigDecimal discountAmount = baseResult.getFinalAmount()
                .multiply(BigDecimal.ONE.subtract(discountRate));
            BigDecimal finalAmount = baseResult.getFinalAmount().multiply(discountRate);
            
            return new CalculationResult(
                baseResult.getOriginalAmount(),
                finalAmount,
                baseResult.getDescription() + ", 会员折扣: " + discountRate + "折"
            );
        }
        
        return baseResult;
    }
}
java 复制代码
// 配置和使用
@Configuration
public class CalculatorConfig {
    
    @Bean
    public AmountCalculator compositeCalculator(CouponService couponService,
                                               PointsService pointsService,
                                               MemberService memberService) {
        // 构建计算链:原价 -> 会员折扣 -> 优惠券 -> 积分抵扣
        AmountCalculator calculator = new BaseAmountCalculator();
        calculator = new MemberDiscountCalculatorDecorator(calculator, memberService);
        calculator = new CouponCalculatorDecorator(calculator, couponService);
        calculator = new PointsCalculatorDecorator(calculator, pointsService);
        
        return calculator;
    }
}

配置类利用spring启动加载一次初始化完成时构建好了完整的装饰器链,并把calculator这个对象交给spring进行管理。

如果不好理解,可以使用另一种方式。

java 复制代码
// 方法链方式(更易读)
public class CalculatorBuilder {
    private AmountCalculator calculator;
    
    public CalculatorBuilder base() {
        this.calculator = new BaseAmountCalculator();
        return this;
    }
    
    public CalculatorBuilder withMemberDiscount(MemberService service) {
        this.calculator = new MemberDiscountCalculatorDecorator(calculator, service);
        return this;
    }
    
    public CalculatorBuilder withCoupon(CouponService service) {
        this.calculator = new CouponCalculatorDecorator(calculator, service);
        return this;
    }
    
    public CalculatorBuilder withPoints(PointsService service) {
        this.calculator = new PointsCalculatorDecorator(calculator, service);
        return this;
    }
    
    public AmountCalculator build() {
        return calculator;
    }
}

// 使用方式(更清晰)
AmountCalculator calculator = new CalculatorBuilder()
    .base()
    .withMemberDiscount(memberService)
    .withCoupon(couponService)
    .withPoints(pointsService)
    .build();
java 复制代码
// 业务使用
@Service
public class OrderService {
    private final AmountCalculator amountCalculator;
    
    public CreateOrderResult createOrder(CreateOrderRequest request) {
        Order order = buildOrder(request);
        
        // 自动应用所有优惠规则
        CalculationResult result = amountCalculator.calculate(order);
        
        order.setFinalAmount(result.getFinalAmount());
        order.setDiscountDescription(result.getDescription());
        
        return saveOrder(order);
    }
}

OrderService进行bean依赖注入,通过构造器注入。在OderService实例化时完成。

相关推荐
qq_364682157 分钟前
录音APP怎么切换语言?多语言版本如何使用
java·服务器·前端
Wang's Blog12 分钟前
Java 项目实战: 交友项目 - 用户体系集成:根据环信id查询用户详情
java·lua·交友
摇滚侠16 分钟前
《On Java 中文版 基础卷》阅读笔记 复用 13
java·笔记·python
2601_9689007719 分钟前
玻璃深加工的工艺窗口:4 道工序的温度与时间参数边界
android·java·javascript
薛之谦_25 分钟前
用 Java 实现阿里云式 IAM 子用户管理:架构设计与表结构详解
java·python·阿里云
甜到心里的蛋糕1 小时前
抖音评论自动回复机器人部署实践:无头 Chrome + DeepSeek + NapCat(2C2G 云主机)
java·jvm·chrome·python·抖音·deepseek
Java爱好狂.1 小时前
阿里1658页Java面试题总结(含答案)
java·后端·高并发·架构师·java面试·java面试题·java八股文
Thanks_ks1 小时前
从 0.1+0.2 不等于 0.3 说起:聊聊浮点数精度问题
java·python·数值计算·精度丢失·bigdecimal·浮点数·ieee754
海鸥-w1 小时前
区域名称同步存储实现笔记
java·笔记
Rain的Java大神之路1 小时前
LeetCode 21 合并两个有序链表:一道 Easy 题,凭什么刷掉一半候选人?
java·后端·算法