给代码穿上品如的衣服:装饰器模式的套娃艺术

给代码穿上品如的衣服:装饰器模式的套娃艺术


一、当继承开始「套娃」

你是否经历过这样的崩溃?

想给咖啡加奶加糖,却被迫创建了SugarMilkCoffeeSugarOnlyCoffee等48个子类;

要给游戏角色装备皮肤,结果代码里长出了RedHatBlueSwordArmorHero这样的类名...

这时你需要装饰器模式------它像品如的衣柜,让对象可以随时随地换皮肤,还能把十层Buff叠得比千层饼还优雅。


二、俄罗斯套娃的设计图(UML图)

java 复制代码
          ┌─────────────┐
          │  Component  │
          ├─────────────┤
          │ +operation()│
          └──────△──────┘
                 │
   ┌─────────────┴─────────────┐
   │                           │
┌─────────────┐        ┌─────────────┐
│ Concrete    │        │ Decorator   │
│ Component   │        ├─────────────┤
└─────────────┘        │ -component  │
                       │ +operation()│
                       └──────△──────┘
                              │
                 ┌────────────┴────────────┐
                 │                         │
          ┌─────────────┐          ┌─────────────┐
          │ Concrete    │          │ Concrete    │
          │ DecoratorA  │          │ DecoratorB  │
          └─────────────┘          └─────────────┘
  • 素颜女神(Component):定义核心功能
  • 基础款(ConcreteComponent):实现基础功能
  • 美妆博主(Decorator):持有组件引用,提供增强接口
  • 彩妆单品(ConcreteDecorator):具体增强实现

三、咖啡店の魔法时刻(代码实战)

1. 基础咖啡(素颜版)
java 复制代码
// 咖啡接口(Component)
interface Coffee {
    String getDesc();
    double cost();
}

// 基础咖啡(ConcreteComponent)
class SimpleCoffee implements Coffee {
    public String getDesc() { return "咖啡"; }
    public double cost() { return 8.0; }
}
2. 魔法装饰器(美妆版)
java 复制代码
// 装饰器基类(Decorator)
abstract class CoffeeDecorator implements Coffee {
    protected Coffee decoratedCoffee;
    
    public CoffeeDecorator(Coffee coffee) {
        this.decoratedCoffee = coffee;
    }
    
    public String getDesc() {
        return decoratedCoffee.getDesc();
    }
    
    public double cost() {
        return decoratedCoffee.cost();
    }
}

// 摩卡魔法(具体装饰器)
class MochaDecorator extends CoffeeDecorator {
    public MochaDecorator(Coffee coffee) {
        super(coffee);
    }
    
    @Override
    public String getDesc() {
        return super.getDesc() + "+摩卡";
    }
    
    @Override
    public double cost() {
        return super.cost() + 3.0;
    }
}

// 香草咒语(具体装饰器)
class VanillaDecorator extends CoffeeDecorator {
    public VanillaDecorator(Coffee coffee) {
        super(coffee);
    }
    
    @Override
    public String getDesc() {
        return super.getDesc() + "+香草";
    }
    
    @Override
    public double cost() {
        return super.cost() + 2.5;
    }
}
3. 点一杯魔法咖啡
java 复制代码
Coffee myCoffee = new SimpleCoffee();
myCoffee = new MochaDecorator(myCoffee);  // 叠加摩卡
myCoffee = new VanillaDecorator(myCoffee);// 叠加香草

System.out.println(myCoffee.getDesc());  // 咖啡+摩卡+香草
System.out.println("价格:" + myCoffee.cost()); // 13.5元

四、装饰器 vs 继承:美颜相机 vs 整容医院

维度 装饰器模式 继承
扩展方式 动态组合 静态编译
类数量 线性增长(N+M) 指数爆炸(M^N)
灵活性 运行时自由搭配 编译时确定
代码复用 装饰器可重复使用 每个组合都是新类
现实类比 乐高积木 石膏雕像

选型指南

  • 需要动态增减功能 → 装饰器
  • 功能组合固定 → 继承
  • 不确定时 → 选装饰器保平安

五、现实世界的叠Buff艺术

  1. Java IO流

    java 复制代码
    // 经典套娃三连
    new BufferedReader(
        new InputStreamReader(
            new FileInputStream("data.txt")
        )
    );
  2. Web中间件:身份验证+日志记录+缓存装饰器链

  3. 游戏装备系统:武器基础属性+宝石强化+皮肤特效

  4. 前端高阶组件:Redux的connect()装饰React组件

  5. Spring AOP:通过装饰器模式实现切面增强

冷知识

Java的Collections.unmodifiableList()其实是个装饰器,给集合套上「只读盔甲」。


六、防套娃翻车指南

  1. 控制嵌套层数

    java 复制代码
    // 错误示范:套娃十连击
    coffee = new Mocha(new Milk(new Sugar(...(new SimpleCoffee()))));
  2. 保持装饰器透明

    java 复制代码
    // 好的装饰器应该像透明滤镜
    // 不要偷偷修改对象核心状态
  3. 避免循环装饰

    java 复制代码
    // 禁止:装饰器A装饰装饰器B,B又装饰A
    // 这会导致代码进入莫比乌斯环
  4. 优先接口装饰

    java 复制代码
    // 装饰器应该面向接口
    // 而不是具体实现类
  5. 警惕性能损耗

    java 复制代码
    // 每个装饰器都是包装盒
    // 套太多会影响性能

七、品如の衣柜总结大会

装饰器模式就像代码界的奇迹暖暖:

  • :用于动态增强对象功能
  • :保持装饰器单一职责
  • 不要:把装饰器当继承用
  • 不要:忘记装饰器带来的复杂度

当你在Java IO流中看到层层嵌套时,请向这些优雅的装饰器致敬------正是它们让简单的字节流变成了强大的处理管道!

相关推荐
tant1an10 分钟前
Spring Boot 基础入门:从核心配置到 SSMP 整合实战
java·数据库·spring boot·sql·spring
客卿12313 分钟前
力扣--组合,子集--回溯法的再探索--总结回溯法
java·算法·leetcode
小湘西19 分钟前
拓扑排序(Topological Sort)
python·设计模式
用户73440281934220 分钟前
SpringBoot —— 实现邮件、短信的发送功能
后端
写Cpp的小黑黑25 分钟前
WebRTC建立流程详解 - 基于WHEP协议
后端
毕设源码-赖学姐27 分钟前
【开题答辩全过程】以 高校晚查寝系统为例,包含答辩的问题和答案
java
xiaoye370841 分钟前
某大厂java面试题二面20260313
java·开发语言·spring
Full Stack Developme1 小时前
Java -jar 命令 可以有哪些参数设置
java·开发语言·jar
程序员Leo1 小时前
OpenClaw 配置指南:DeepSeek 与 飞书集成
后端·agent
一只程序熊1 小时前
vite-cool-unix-ctx] Unexpected token l in JSON at position 0
java·服务器·前端