业务代码中如何使用装饰器模式?

装饰器模式(Decorator Pattern)介绍

装饰器模式(Decorator Pattern)是一种结构型设计模式,我们可以动态地给一个对象添加额外的职责。而不是通过继承增加子类的方式来扩展对象的功能,装饰器模式使用组合的方式来增强对象的功能,这有助于保持类的职责单一,提高了类的复用性和灵活性。

装饰器模式通常涉及以下角色:

  • • 组件(Component)接口:定义了一个对象接口,可以动态地给这些对象添加职责。
  • • 具体组件(Concrete Component):实现了组件接口的对象,这是我们要动态添加职责的对象。
  • • 装饰器(Decorator)类:实现了组件接口的抽象类,并持有一个组件接口的引用。这个类的任务是定义那些可以动态添加的职责。
  • • 具体装饰器(Concrete Decorator):实现了装饰器类的具体类,负责给组件添加新的职责。

优缺点

优点:

  • • 扩展对象功能:装饰器模式提供了一种灵活的替代方案来扩展对象的功能,比继承更灵活。
  • • 动态添加职责:可以动态地添加或删除对象的职责。
  • • 复合而非继承:通过使用组合而非继承的方式,避免了在层级关系上造成静态的继承关系,提高了代码的可复用性和灵活性。

缺点:

  • • 增加系统复杂性:使用大量的小类,可能会增加系统的复杂度。
  • • 多层装饰比较复杂:当装饰链过长时,会增加系统的复杂度,并且调试时可能会带来不便。

Spring Boot中业务代码示例

假设我们有个下单流程计算价格,现在我们需要新增一个打折优惠。怎么不修改原来代码巧妙的增加我们的功能呢?通过装饰器模式将不同的折扣策略作为装饰层动态添加到订单处理流程中。这样,我们可以在不修改现有类的情况下,灵活地添加或修改折扣策略。

订单接口

首先,定义一个订单服务接口,这是我们的组件接口:

csharp 复制代码
public interface OrderService {
    double applyDiscount(double orderTotal);
}

实现一个基本的订单服务:

java 复制代码
@Component
public class SimpleOrderService implements OrderService {
    @Override
    public double applyDiscount(double orderTotal) {
        // 默认情况下,没有任何折扣
        return orderTotal;
    }
}

装饰器实现

接着,我们创建一个装饰器抽象类:

java 复制代码
@Component
public abstract class OrderServiceDecorator implements OrderService {
    @Resource
    protected OrderService simpleOrderService;

    @Override
    public double applyDiscount(double orderTotal) {
        return simpleOrderService.applyDiscount(orderTotal);
    }
}

定义不同的折扣策略作为装饰器:

java 复制代码
@Component
public class PercentageDiscountDecorator extends OrderServiceDecorator {

    @Override
    public double applyDiscount(double orderTotal) {
        // 折扣率
        double discountRate = 0.2;
        double discount = orderTotal * discountRate;
        return super.applyDiscount(orderTotal - discount);
    }
}

整体结构如下

测试一下

java 复制代码
@SpringBootApplication
public class DecoratorPatternApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(DecoratorPatternApplication.class, args);

        OrderService percentageService = context.getBean(PercentageDiscountDecorator.class);

        double orderTotal = 300.0;
        double discountedTotal = percentageService.applyDiscount(orderTotal);
        System.out.println("Discounted order total: " + discountedTotal);
    }
}

在这个例子中,通过PercentageDiscountDecorator 添加了百分比折扣。通过这种方式,我们可以根据需要灵活地组合和应用不同的折扣策略,而不必更改现有的订单服务代码。这种方法提高了代码的可维护性和扩展性,同时也保持了业务逻辑的清晰性。

与策略模式区别

之前分享过策略模式的实现:如何在业务代码中优雅的使用策略模式

我们回顾下策略模式的特征:策略模式定义了一系列的算法,并将每一个算法封装起来,使它们可以互相替换。策略模式让算法独立于使用它的客户端而变化,即分离了算法的定义和使用,客户端代码通过依赖注入的方式选择使用哪一种策略。

在我们上面的示例中,如果使用装饰器模式,我们可以动态地为订单服务添加额外的功能(如折扣),而每个装饰器都可以独立地添加额外的职责。

而如果使用策略模式来处理折扣,我们可能会定义一个折扣策略接口,然后为每种折扣定义一个具体的策略类。客户端代码决定使用哪一种折扣策略,并将其应用于订单总额上。这里,策略是静态选择的,并且一次只能应用一个策略。

装饰器模式强调的是如何动态地添加职责,而策略模式强调的是如何封装算法和行为,并在它们之间切换。

相关推荐
今夜有雨.3 分钟前
使用C++实现HTTP服务
开发语言·网络·c++·后端·网络协议·tcp/ip·http
东方苾梦7 分钟前
Lua语言的安全开发
开发语言·后端·golang
Asthenia041210 分钟前
Spring Boot @Conditional 注解分析与实际业务场景应用
后端
慕离桑27 分钟前
HTML语言的数据可视化
开发语言·后端·golang
小园子的小菜31 分钟前
深入探究 RocketMQ 中的 Broker2Client 组件
java·rocketmq·java-rocketmq
我命由我1234533 分钟前
C++ - 头文件基础(常用标准库头文件、自定义头文件、头文件引入方式、防止头文件重复包含机制)
服务器·c语言·开发语言·c++·后端·visualstudio·visual studio code
Asthenia041238 分钟前
深入解析BCrypt:原理、应用与面试问题
后端
bing_15841 分钟前
Mybatis 如何自定义缓存?
java·缓存·mybatis
RainbowSea44 分钟前
8. RabbitMQ 消息队列 + 结合配合 Spring Boot 框架实现 “发布确认” 的功能
java·消息队列·rabbitmq
Asthenia04121 小时前
Java中UUID的原理与生成策略
后端