【Java】23种设计模式——9.装饰器模式

概念

装饰器模式 ,是一种结构设计模式。 通过B类,实现A类方法的前后执行操作。类似于AOP。其核心是组合替代继承,避免因多维度扩展导致的子类爆炸问题。

核心角色

角色 职责 示例场景
组件接口(Component‌) 定义被装饰对象和装饰器的共同接口 Coffee(咖啡基础接口)
具体组件(ConcreteComponent‌) 实现 Component,表示基础对象 BlackCoffee(黑咖啡)
装饰器抽象类(Decorator‌) 实现 Component,持有 Component 引用 CoffeeDecorator(装饰器基类)
具体装饰器(ConcreteDecorator‌) 继承 Decorator,添加具体扩展功能 MilkDecorator(加牛奶装饰)

应用场景

  • 动态添加和撤销功能 如为对象添加日志、权限等临时功能。
  • 避免多层继承的复杂结构 当子类数量爆炸时,用装饰器替代继承。
  • 透明扩展对象行为 客户端无需改制装饰器的存在(如Java I/O流)。

使用

设计一个咖啡订单系统,支持动态添加咖啡配料,并动态获取总价和描述。

  • 定义组件接口 (咖啡的抽象接口)

    /**
     * 组件接口 : 咖啡
     * @Author:lyj
     * @Date:2025/5/6 15:59
     */
    public interface Coffee {
        public double getCost();            // 获取
        public String getDescription();     // 获取描述
    }
    
  • 实现具体组件 (咖啡基础:黑咖啡、美式咖啡)

    /**
     * 具体组件 : 黑咖啡
     * @Author:lyj
     * @Date:2025/5/6 16:02
     */
    public class BlackCoffee implements Coffee {
        /**
         * 获取咖啡价格
         * @return
         */
        @Override
        public double getCost() {
            return 2.0;     // 基础价格
        }
    
        /**
         * 获取咖啡描述
         * @return
         */
        @Override
        public String getDescription() {
            return "黑咖啡";
        }
    }
    /**
     * 具体组件:美式咖啡
     * @Author:lyj
     * @Date:2025/5/7 10:48
     */
    public class Americano implements Coffee{
        /**
         * 获取咖啡价格
         * @return
         */
        @Override
        public double getCost() {
            return 2.2;         // 基础价格
        }
    
        /**
         * 获取咖啡描述
         * @return
         */
        @Override
        public String getDescription() {
            return "美式咖啡";
        }
    }
    
  • 定义装饰器抽象类(包装组件) (咖啡 + 配料)

    /**
     * 装饰器抽象类(持有装饰器) : 咖啡+配料
     * 持有被装饰对象的引用
     * @Author:lyj
     * @Date:2025/5/7 10:50
     */
    public abstract class CoffeeDecorator implements Coffee {
        protected Coffee coffee;        // 被装饰的咖啡对象
    
        public CoffeeDecorator(Coffee coffee) {
            this.coffee = coffee;
        }
    
        /**
         * 获取咖啡价格
         * 默认直接调用被装饰对象的方法
         * @return
         */
        @Override
        public double getCost() {
            return coffee.getCost();
        }
    
        @Override
        public String getDescription() {
            return coffee.getDescription();
        }
    }
    
  • 实现具体装饰器 (加糖+ 加奶)

    /**
     * 具体装饰器 : 添加牛奶
     * @Author:lyj
     * @Date:2025/5/7 14:05
     */
    public class MilkDecorator extends CoffeeDecorator {
        public MilkDecorator(Coffee coffee) {
            super(coffee);
        }
    
        /**
         * 获取价格
         * @return
         */
        @Override
        public double getCost() {
            return super.getCost() + 0.5;
        }
    
        @Override
        public String getDescription() {
            return super.getDescription() + "+牛奶";
        }
    }
    
    /**
     * 具体装饰器 : 添加糖
     * @Author:lyj
     * @Date:2025/5/7 14:05
     */
    public class SugurDecorator extends CoffeeDecorator {
        public SugurDecorator(Coffee coffee) {
            super(coffee);
        }
    
        /**
         * 获取价格
         * @return
         */
        @Override
        public double getCost() {
            return super.getCost() + 0.4;
        }
    
        @Override
        public String getDescription() {
            return super.getDescription() + "+糖";
        }
    }
    

测试运行

// 基础黑咖啡
Coffee coffee = new BlackCoffee();
System.out.println(coffee.getDescription() + ": " + coffee.getCost());      // 黑咖啡: 2.0
// 加上牛奶
Coffee coffee1 = new MilkDecorator(coffee);
System.out.println(coffee1.getDescription() + ": " + coffee1.getCost());    // 黑咖啡+牛奶: 2.5
// 加糖
Coffee coffee2 = new SugurDecorator(coffee1);
System.out.println(coffee2.getDescription() + ": " + coffee2.getCost());    // 黑咖啡+牛奶+糖: 2.9

// 基础美式咖啡
Coffee coffee3 = new Americano();
System.out.println(coffee3.getDescription() + ": " + coffee3.getCost()); // 美式咖啡: 2.2
// 加糖
Coffee coffee4 = new SugurDecorator(coffee3);
System.out.println(coffee4.getDescription() + ": " + coffee4.getCost());    // 美式咖啡+糖: 2.6
// 加牛奶
Coffee coffee5 = new MilkDecorator(coffee4);
System.out.println(coffee5.getDescription() + ": " + coffee5.getCost());    // 美式咖啡+糖+牛奶: 3.1

Java 标准库中的应用装饰器

Java I/O流失装饰器的经典案例

// 基础组件:FileInputStream
InputStream in = new FileInputStream("test.txt");

// 装饰器:BufferedInputStream(添加缓冲功能)
InputStream bufferedIn = new BufferedInputStream(in);

// 装饰器:DataInputStream(添加数据类型读取功能)
InputStream dataIn = new DataInputStream(bufferedIn);

装饰器模式和注解模式区别

注解的使用,可查看:https://www.cnblogs.com/luyj00436/p/18544203

对比点 装饰器模式 注解模式
本质 行为增强工具 元数据标记工具
执行主体 自身实现逻辑 依赖外部处理器
扩展维度 功能叠加(横向扩展) 配置标记(纵向解耦)

适用选择建议:

  • 需要动态叠加功能(如日志、缓存)→优先使用装饰器模式
  • 需要标记配置信息(如权限、路由)→优先使用注解模式。

装饰器模式使用场景例子

  • Java I/O 流增强BufferedInputStream包装FileInputStream 添加缓冲功能。
  • UI 组件扩展。 动态为GUI添加边框、阴影等视觉效果。
  • 电商促销叠加。 基础价格通过DiscountDecorator 叠加满减、优惠券等促销规则。
  • 日志记录增强 。 在和兴业务逻辑外层包装日记装饰器,记录方法时间。
  • 权限校验链 。 多层装饰器实现多重校验(角色校验+IP白名单)
  • 缓存机制扩展。 为数据库查询添加 CacheDecorator 实现结果缓存。
  • 数据压缩处理。 使用GzipDecorator 包装网络传输流实现自动压缩。
  • 加密解密扩展。 在数据库存储前通过EncrypDecorator 自动加密敏感字段。
  • 消息格式转换。 为消息处理添加XMLFormatterDecorator 实现格式转换。
  • 游戏角色装备系统 。 动态为角色添加武器、护甲等装备效果(属性值动态计算)。

注解模式使用场景

元数据驱动场景

  • 依赖注入标记。 Spring 的@Autowired 自动注入 Bean实例。
  • 单元测试标识 。 JUnit的@Test标记测试方法。
  • API文档生成。 Swagger 的@Api 注解生成接口文档描述。
  • 路由器映射控制 Spring MVC 的@RequestMapping,定义HTTP请求路径。
  • 代码生成简化 Lomlok的@Data自动生成 Getter/Setter方法。
  • 权限控制拦截 Spring Securify 的@PreAuthorize 标记接口访问权限。
  • 数据校验标记 JSR-303的@Valid 校验请求参数合法性。
  • JSON序列化控制 Jackson的@JsonIgnore忽略字段序列化。
  • 定时任务调度 Spring的@Scheduled 定义定时任务执行规则。
  • ORM实体映射 Hibernate的@Entry标记数据库表的映射关系
posted @ 2025-06-20 15:43  陆陆无为而治者  阅读(60)  评论(0)    收藏  举报