17装饰器模式

装饰器模式 (Decorator Pattern)

什么是装饰器模式?

装饰器模式是一种结构型设计模式,它允许你在不改变对象结构的情况下动态地给对象添加新的功能

简单来说:装饰器模式就是给对象"穿衣服",可以一层一层地添加功能。

生活中的例子

想象一下:

  • 穿衣服:先穿内衣,再穿衬衫,再穿外套,最后穿大衣
  • 装饰蛋糕:先做蛋糕,再涂奶油,再放水果,最后放蜡烛
  • 装修房子:先刷墙,再铺地板,再买家具,最后挂画

为什么需要装饰器模式?

传统方式的问题

java 复制代码
// 使用继承会导致类爆炸
class SimpleCoffee {}
class MilkCoffee extends SimpleCoffee {}
class SugarCoffee extends SimpleCoffee {}
class MilkSugarCoffee extends SimpleCoffee {}
// ... 更多组合

问题

  1. 类爆炸:多个功能组合会导致类数量爆炸
  2. 扩展困难:新增功能需要创建多个类
  3. 灵活性差:无法动态添加或移除功能

装饰器模式的优势

java 复制代码
// 动态添加功能
Coffee coffee = new SimpleCoffee();
coffee = new MilkDecorator(coffee);
coffee = new SugarDecorator(coffee);

优势

  1. 动态添加:可以动态地添加或移除功能
  2. 灵活组合:可以灵活地组合多个功能
  3. 避免类爆炸:避免因继承导致的类爆炸

装饰器模式的结构

复制代码
┌─────────────────────┐
│    Component        │  组件接口
├─────────────────────┤
│ + operation(): void │
└──────────┬──────────┘
           │ 实现
           ├──┬──────────────────┬──────────────┐
           │                    │              │
┌──────────┴──────┐  ┌───────────┴───────┐  ┌───┴────────┐
│ ConcreteComp     │  │    Decorator      │  │ ...       │
├─────────────────┤  ├───────────────────┤  ├────────────┤
│ + operation()   │  │ - component: Comp │  │            │
└─────────────────┘  │ + operation()     │  │            │
                     └──────────┬────────┘  │            │
                                │ 实现      │            │
                    ┌───────────┴────────┐  │            │
                    │ ConcreteDecorator  │  │            │
                    ├───────────────────┤  │            │
                    │ + operation()     │  │            │
                    └───────────────────┘  └────────────┘

代码示例

1. 定义组件接口

java 复制代码
/**
 * 咖啡接口
 */
public interface Coffee {
    /**
     * 计算价格
     */
    double cost();
    
    /**
     * 获取描述
     */
    String description();
}

2. 定义具体组件

java 复制代码
/**
 * 具体组件:简单咖啡
 */
public class SimpleCoffee implements Coffee {
    @Override
    public double cost() {
        return 10.0;
    }
    
    @Override
    public String description() {
        return "简单咖啡";
    }
}

3. 定义装饰器

java 复制代码
/**
 * 抽象装饰器:咖啡装饰器
 */
public abstract class CoffeeDecorator implements Coffee {
    protected Coffee coffee;
    
    public CoffeeDecorator(Coffee coffee) {
        this.coffee = coffee;
    }
    
    @Override
    public double cost() {
        return coffee.cost();
    }
    
    @Override
    public String description() {
        return coffee.description();
    }
}

4. 定义具体装饰器

java 复制代码
/**
 * 具体装饰器:牛奶
 */
public class MilkDecorator extends CoffeeDecorator {
    public MilkDecorator(Coffee coffee) {
        super(coffee);
    }
    
    @Override
    public double cost() {
        return coffee.cost() + 2.0;
    }
    
    @Override
    public String description() {
        return coffee.description() + " + 牛奶";
    }
}

/**
 * 具体装饰器:糖
 */
public class SugarDecorator extends CoffeeDecorator {
    public SugarDecorator(Coffee coffee) {
        super(coffee);
    }
    
    @Override
    public double cost() {
        return coffee.cost() + 1.0;
    }
    
    @Override
    public String description() {
        return coffee.description() + " + 糖";
    }
}

/**
 * 具体装饰器:奶油
 */
public class WhipDecorator extends CoffeeDecorator {
    public WhipDecorator(Coffee coffee) {
        super(coffee);
    }
    
    @Override
    public double cost() {
        return coffee.cost() + 3.0;
    }
    
    @Override
    public String description() {
        return coffee.description() + " + 奶油";
    }
}

5. 使用装饰器

java 复制代码
/**
 * 装饰器模式测试类
 * 演示如何使用装饰器模式动态地给对象添加功能
 */
public class DecoratorTest {
    
    public static void main(String[] args) {
        System.out.println("=== 装饰器模式测试 ===\n");
        
        // 创建简单咖啡
        Coffee coffee = new SimpleCoffee();
        System.out.println(coffee.description() + ": ¥" + coffee.cost());
        
        // 添加牛奶
        coffee = new MilkDecorator(coffee);
        System.out.println(coffee.description() + ": ¥" + coffee.cost());
        
        // 添加糖
        coffee = new SugarDecorator(coffee);
        System.out.println(coffee.description() + ": ¥" + coffee.cost());
        
        // 添加奶油
        coffee = new WhipDecorator(coffee);
        System.out.println(coffee.description() + ": ¥" + coffee.cost());
        
        System.out.println("\n--- 创建不同的组合 ---");
        
        // 只加牛奶和糖
        Coffee coffee1 = new MilkDecorator(new SugarDecorator(new SimpleCoffee()));
        System.out.println(coffee1.description() + ": ¥" + coffee1.cost());
        
        // 只加奶油
        Coffee coffee2 = new WhipDecorator(new SimpleCoffee());
        System.out.println(coffee2.description() + ": ¥" + coffee2.cost());
        
        // 全部添加
        Coffee coffee3 = new WhipDecorator(new SugarDecorator(new MilkDecorator(new SimpleCoffee())));
        System.out.println(coffee3.description() + ": ¥" + coffee3.cost());
        
        System.out.println("\n=== 装饰器模式的优势 ===");
        System.out.println("1. 动态添加:可以动态地添加或移除功能");
        System.out.println("2. 灵活组合:可以灵活地组合多个功能");
        System.out.println("3. 避免类爆炸:避免因继承导致的类爆炸");
        System.out.println("4. 单一职责:每个装饰器只负责一个功能");
        
        System.out.println("\n=== 实际应用场景 ===");
        System.out.println("1. IO流:Java IO流中的BufferedReader、LineNumberReader等");
        System.out.println("2. UI组件:给按钮添加边框、滚动条等");
        System.out.println("3. 日志记录:给日志添加时间戳、级别等");
        System.out.println("4. 数据处理:给数据添加压缩、加密等功能");
    }
}

装饰器模式的优点

  1. 动态添加:可以动态地添加或移除功能
  2. 灵活组合:可以灵活地组合多个功能
  3. 避免类爆炸:避免因继承导致的类爆炸
  4. 单一职责:每个装饰器只负责一个功能

装饰器模式的缺点

  1. 增加复杂度:引入了多个小类
  2. 调试困难:多层装饰会导致调试困难

适用场景

  1. 动态添加功能:需要动态地添加或移除功能
  2. 功能组合:需要灵活地组合多个功能
  3. 避免类爆炸:需要避免因继承导致的类爆炸

常见应用场景

  • IO流:Java IO流中的BufferedReader、LineNumberReader等
  • UI组件:给按钮添加边框、滚动条等
  • 日志记录:给日志添加时间戳、级别等

使用建议

  • 动态添加功能:使用装饰器模式
  • 功能组合:使用装饰器模式
  • 固定功能:直接使用继承即可

注意事项

⚠️ 装饰器模式虽然强大,但要注意:

  • 不要过度装饰,增加不必要的复杂度
  • 保持装饰器的顺序性
相关推荐
呱呱复呱呱6 小时前
Django CBV 源码解读:一个请求是怎么找到你的 get() 方法的
python·django
曲幽11 小时前
刚部署的 LibreTranslate 频频翻车?我掏出了 20 年前的 StarDict 词典,用 FastAPI 搭了个本地词典翻译 API
python·fastapi·web·translate·goldendict·libretranslate·stardict·pystardict
荣码11 小时前
用Streamlit给AI应用套个界面,10行代码出Web页面
java·python
兵慌码乱21 小时前
基于Python+PyQt5+SQLite的药房管理系统实现:事务一致性与界面解耦全流程解析
python·sqlite·信号与槽·pyqt5·数据库设计·桌面应用开发·事务处理
金銀銅鐵1 天前
[Python] 体验用欧几里得算法计算最大公约数的过程
python·数学
FreakStudio1 天前
W55MH32L-EVB 上手测评:硬件 TCP/IP 加持的以太网单片机,MicroPython 零门槛开发
python·单片机·嵌入式·大学生·面向对象·并行计算·电子diy·电子计算机
用户0332126663671 天前
使用 Python 从零创建 Word 文档
python
Csvn1 天前
Python 两大经典坑点 —— 可变默认参数 & 闭包延迟绑定
后端·python
曲幽1 天前
别再用网页翻译看源码了!你的私人翻译神器LibreTranslate,部署避坑指南来了
python·docker·web·pot·translate·libretranslate·arogstranslate
用户556918817531 天前
#从脚本到独立程序:Python + Playwright 批量抓取的完整踩坑记录
python·自动化运维