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组件:给按钮添加边框、滚动条等
  • 日志记录:给日志添加时间戳、级别等

使用建议

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

注意事项

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

  • 不要过度装饰,增加不必要的复杂度
  • 保持装饰器的顺序性
相关推荐
gf13211112 小时前
python_生成RPA运行数据报告
windows·python·rpa
嫂子开门我是_我哥2 小时前
第八节:条件判断与循环:解锁Python的逻辑控制能力
开发语言·python
java1234_小锋2 小时前
什么是Java可重入锁?
java·开发语言
小尧嵌入式2 小时前
【Linux开发五】条件变量|信号量|生产者消费者模型|信号概念和常见信号|信号的使用和处理
linux·运维·服务器·开发语言·c++·嵌入式硬件
不会c+2 小时前
Spring详解(二)IOC控制反转
java·开发语言
翔云1234562 小时前
golang中使用 sort.Interface 实现复杂多级排序
开发语言·后端·golang
2301_805962932 小时前
树莓派的一些问题记录-1:usbboot仓库
python·gitee
深蓝电商API2 小时前
Selenium 动作链 ActionChains 高级用法
爬虫·python·selenium
「QT(C++)开发工程师」2 小时前
C++设计模式
开发语言·c++·设计模式