23种设计模式23种设计模式
首页
介绍
  • 单例模式
  • 工厂方法模式
  • 抽象工厂模式
  • 建造者模式
  • 原型模式
  • 适配器模式
  • 桥接模式
  • 组合模式
  • 装饰器模式
  • 外观模式
  • 享元模式
  • 代理模式
  • 责任链模式
  • 命令模式
  • 解释器模式
  • 迭代器模式
  • 中介者模式
  • 备忘录模式
  • 观察者模式
  • 状态模式
  • 策略模式
  • 模板方法模式
  • 访问者模式
🚀 编程指南
首页
介绍
  • 单例模式
  • 工厂方法模式
  • 抽象工厂模式
  • 建造者模式
  • 原型模式
  • 适配器模式
  • 桥接模式
  • 组合模式
  • 装饰器模式
  • 外观模式
  • 享元模式
  • 代理模式
  • 责任链模式
  • 命令模式
  • 解释器模式
  • 迭代器模式
  • 中介者模式
  • 备忘录模式
  • 观察者模式
  • 状态模式
  • 策略模式
  • 模板方法模式
  • 访问者模式
🚀 编程指南
  • 结构型模式

    • 适配器模式 (Adapter)
    • 桥接模式 (Bridge)
    • 组合模式 (Composite)
    • 装饰器模式 (Decorator)
    • 外观模式 (Facade)
    • 享元模式 (Flyweight)
    • 代理模式 (Proxy)

装饰器模式 (Decorator)

📖 通俗理解

想象你去咖啡店买咖啡:

  • 基础款:美式咖啡 15元
  • 加牛奶:+5元
  • 加糖:+2元
  • 加奶泡:+3元

你可以自由组合:美式 + 牛奶 + 糖 = 22元

装饰器模式就是:动态地给对象添加额外的功能,而不需要修改原有类。就像给咖啡加料一样,一层层"装饰"上去。

🎯 解决什么问题?

问题:如果用继承来扩展功能,会导致类爆炸。

咖啡
├── 美式
│   ├── 美式+牛奶
│   ├── 美式+糖
│   ├── 美式+牛奶+糖
│   └── ...
├── 拿铁
│   ├── 拿铁+牛奶
│   └── ...
└── ...

继承是静态的,在编译时就确定了。而装饰器是动态的,可以在运行时自由组合。

🌰 生活中的例子

  • 咖啡加料:基础咖啡 + 牛奶 + 糖
  • 手机壳:手机 + 保护壳 + 挂绑
  • 穿衣服:人 + 衬衫 + 外套 + 围巾
  • 汉堡:面包 + 肉饼 + 生菜 + 酱料

💻 Java 代码实现

场景:咖啡店

第一步:定义组件接口

/**
 * 组件接口:饮料
 */
public interface Beverage {
    String getDescription();  // 描述
    double getCost();         // 价格
}

第二步:实现具体组件(基础咖啡)

/**
 * 具体组件:美式咖啡
 */
public class Americano implements Beverage {
    
    @Override
    public String getDescription() {
        return "美式咖啡";
    }
    
    @Override
    public double getCost() {
        return 15.0;
    }
}

/**
 * 具体组件:拿铁
 */
public class Latte implements Beverage {
    
    @Override
    public String getDescription() {
        return "拿铁";
    }
    
    @Override
    public double getCost() {
        return 20.0;
    }
}

/**
 * 具体组件:卡布奇诺
 */
public class Cappuccino implements Beverage {
    
    @Override
    public String getDescription() {
        return "卡布奇诺";
    }
    
    @Override
    public double getCost() {
        return 22.0;
    }
}

第三步:定义装饰器抽象类

/**
 * 装饰器抽象类
 * 关键:继承 Beverage 并持有 Beverage 引用
 */
public abstract class CondimentDecorator implements Beverage {
    
    protected Beverage beverage;  // 被装饰的对象
    
    public CondimentDecorator(Beverage beverage) {
        this.beverage = beverage;
    }
    
    @Override
    public String getDescription() {
        return beverage.getDescription();
    }
    
    @Override
    public double getCost() {
        return beverage.getCost();
    }
}

第四步:实现具体装饰器(配料)

/**
 * 具体装饰器:牛奶
 */
public class Milk extends CondimentDecorator {
    
    public Milk(Beverage beverage) {
        super(beverage);
    }
    
    @Override
    public String getDescription() {
        return beverage.getDescription() + " + 牛奶";
    }
    
    @Override
    public double getCost() {
        return beverage.getCost() + 5.0;
    }
}

/**
 * 具体装饰器:糖
 */
public class Sugar extends CondimentDecorator {
    
    public Sugar(Beverage beverage) {
        super(beverage);
    }
    
    @Override
    public String getDescription() {
        return beverage.getDescription() + " + 糖";
    }
    
    @Override
    public double getCost() {
        return beverage.getCost() + 2.0;
    }
}

/**
 * 具体装饰器:奶泡
 */
public class MilkFoam extends CondimentDecorator {
    
    public MilkFoam(Beverage beverage) {
        super(beverage);
    }
    
    @Override
    public String getDescription() {
        return beverage.getDescription() + " + 奶泡";
    }
    
    @Override
    public double getCost() {
        return beverage.getCost() + 3.0;
    }
}

/**
 * 具体装饰器:巧克力
 */
public class Chocolate extends CondimentDecorator {
    
    public Chocolate(Beverage beverage) {
        super(beverage);
    }
    
    @Override
    public String getDescription() {
        return beverage.getDescription() + " + 巧克力";
    }
    
    @Override
    public double getCost() {
        return beverage.getCost() + 4.0;
    }
}

第五步:使用装饰器

public class CoffeeShop {
    public static void main(String[] args) {
        // 1. 纯美式咖啡
        Beverage coffee1 = new Americano();
        System.out.println(coffee1.getDescription() + " = ¥" + coffee1.getCost());
        
        // 2. 美式 + 牛奶
        Beverage coffee2 = new Americano();
        coffee2 = new Milk(coffee2);
        System.out.println(coffee2.getDescription() + " = ¥" + coffee2.getCost());
        
        // 3. 拿铁 + 糖 + 奶泡
        Beverage coffee3 = new Latte();
        coffee3 = new Sugar(coffee3);
        coffee3 = new MilkFoam(coffee3);
        System.out.println(coffee3.getDescription() + " = ¥" + coffee3.getCost());
        
        // 4. 卡布奇诺 + 双倍巧克力 + 牛奶
        Beverage coffee4 = new Cappuccino();
        coffee4 = new Chocolate(coffee4);
        coffee4 = new Chocolate(coffee4);  // 双倍巧克力
        coffee4 = new Milk(coffee4);
        System.out.println(coffee4.getDescription() + " = ¥" + coffee4.getCost());
    }
}

输出:

美式咖啡 = ¥15.0
美式咖啡 + 牛奶 = ¥20.0
拿铁 + 糖 + 奶泡 = ¥25.0
卡布奇诺 + 巧克力 + 巧克力 + 牛奶 = ¥35.0

🔥 实战案例:IO 流装饰器

Java IO 是装饰器模式的经典应用!

import java.io.*;

public class IODecoratorDemo {
    public static void main(String[] args) throws Exception {
        // 最基础的字节流
        InputStream in = new FileInputStream("data.txt");
        
        // 加一层缓冲功能
        in = new BufferedInputStream(in);
        
        // 再加一层数据读取功能
        DataInputStream dataIn = new DataInputStream(in);
        
        // 现在可以直接读取基本类型
        int number = dataIn.readInt();
        
        // 一层层装饰!
    }
}

Java IO 的装饰器结构:

InputStream(抽象组件)
├── FileInputStream(具体组件)
├── ByteArrayInputStream(具体组件)
└── FilterInputStream(装饰器基类)
    ├── BufferedInputStream(具体装饰器)
    ├── DataInputStream(具体装饰器)
    └── ...

🔥 实战案例:日志增强

/**
 * 日志接口
 */
public interface Logger {
    void log(String message);
}

/**
 * 基础日志实现
 */
public class BasicLogger implements Logger {
    @Override
    public void log(String message) {
        System.out.println(message);
    }
}

/**
 * 日志装饰器基类
 */
public abstract class LoggerDecorator implements Logger {
    protected Logger logger;
    
    public LoggerDecorator(Logger logger) {
        this.logger = logger;
    }
    
    @Override
    public void log(String message) {
        logger.log(message);
    }
}

/**
 * 时间戳装饰器
 */
public class TimestampDecorator extends LoggerDecorator {
    
    public TimestampDecorator(Logger logger) {
        super(logger);
    }
    
    @Override
    public void log(String message) {
        String timestamp = java.time.LocalDateTime.now().toString();
        logger.log("[" + timestamp + "] " + message);
    }
}

/**
 * 日志级别装饰器
 */
public class LevelDecorator extends LoggerDecorator {
    
    private String level;
    
    public LevelDecorator(Logger logger, String level) {
        super(logger);
        this.level = level;
    }
    
    @Override
    public void log(String message) {
        logger.log("[" + level + "] " + message);
    }
}

/**
 * 加密装饰器
 */
public class EncryptDecorator extends LoggerDecorator {
    
    public EncryptDecorator(Logger logger) {
        super(logger);
    }
    
    @Override
    public void log(String message) {
        // 简单加密示例
        String encrypted = "ENCRYPTED[" + message.hashCode() + "]";
        logger.log(encrypted);
    }
}

使用方式:

public class LoggerDemo {
    public static void main(String[] args) {
        // 基础日志
        Logger logger1 = new BasicLogger();
        logger1.log("普通日志");
        
        System.out.println();
        
        // 加时间戳
        Logger logger2 = new TimestampDecorator(new BasicLogger());
        logger2.log("带时间戳的日志");
        
        System.out.println();
        
        // 加时间戳 + 日志级别
        Logger logger3 = new LevelDecorator(
            new TimestampDecorator(new BasicLogger()), 
            "INFO"
        );
        logger3.log("带级别的日志");
        
        System.out.println();
        
        // 加时间戳 + 级别 + 加密
        Logger logger4 = new EncryptDecorator(
            new LevelDecorator(
                new TimestampDecorator(new BasicLogger()),
                "ERROR"
            )
        );
        logger4.log("敏感信息");
    }
}

输出:

普通日志

[2024-01-15T10:30:45.123] 带时间戳的日志

[INFO] [2024-01-15T10:30:45.125] 带级别的日志

ENCRYPTED[-1234567890]

📊 类图结构

        ┌────────────────┐
        │   Component    │ ◄────────────────┐
        │ + operation()  │                  │
        └───────▲────────┘                  │
                │                           │
    ┌───────────┴───────────┐               │
    │                       │               │
┌───┴────────┐      ┌───────┴───────┐       │
│ Concrete   │      │   Decorator   │ ──────┘
│ Component  │      │ -component    │  持有引用
│+operation()│      │ +operation()  │
└────────────┘      └───────▲───────┘
                            │
               ┌────────────┴────────────┐
               │                         │
       ┌───────┴───────┐         ┌───────┴───────┐
       │ DecoratorA    │         │ DecoratorB    │
       │ +operation()  │         │ +operation()  │
       └───────────────┘         └───────────────┘

⚠️ 装饰器 vs 继承

对比项继承装饰器
扩展时机编译时运行时
灵活性低高
组合方式固定自由组合
类的数量会爆炸可控

✅ 适用场景

  1. 动态添加功能:在不修改原类的情况下扩展功能
  2. 功能可选:功能可以任意组合
  3. 代替继承:继承层次过深时

⚠️ 注意事项

  1. 装饰器和被装饰对象必须实现相同的接口
  2. 装饰器持有被装饰对象的引用
  3. 装饰顺序可能影响结果

小结

装饰器模式的核心:通过包装的方式,动态地给对象添加功能。

关键特点:

  • 装饰器和被装饰者实现同一接口
  • 装饰器持有被装饰者的引用
  • 可以层层嵌套,像穿衣服一样

👉 下一篇:外观模式

Prev
组合模式 (Composite)
Next
外观模式 (Facade)