装饰器模式 (Decorator Pattern)

装饰器模式 (Decorator Pattern)

概述

装饰器模式是一种结构型设计模式,它动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。

意图

  • 动态地给一个对象添加一些额外的职责
  • 就增加功能来说,装饰器模式相比生成子类更为灵活

适用场景

  • 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责
  • 处理那些可以撤销的职责
  • 当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数量呈爆炸性增长
  • 当需要对已有对象进行功能增强,但又不想通过继承来实现时

结构

复制代码
┌─────────────┐          ┌─────────────┐
│  Component  │<─────────│   Client    │
├─────────────┤          ├─────────────┤
│ + operation()│          │             │
└─────────────┘          └─────────────┘
        ▲
        │
┌─────────────┐          ┌─────────────┐
│ConcreteComponent│       │  Decorator  │
├─────────────┤          ├─────────────┤
│ + operation()│          │ - component │
└─────────────┘          │ + operation()│
                         └─────────────┘
                                 ▲
                                 │
┌─────────────┐          ┌─────────────┐
│ConcreteDecoratorA│      │ConcreteDecoratorB│
├─────────────┤          ├─────────────┤
│ + operation()│          │ + operation()│
│ + addedBehavior()│      │ + addedState() │
└─────────────┘          └─────────────┘

参与者

  • Component:定义一个对象接口,可以给这些对象动态地添加职责
  • ConcreteComponent:定义一个具体的对象,也可以给这个对象添加一些职责
  • Decorator:维持一个指向Component对象的引用,并定义一个与Component接口一致的接口
  • ConcreteDecorator:向组件添加职责

示例代码

下面是一个完整的装饰器模式示例,以咖啡店为例:

java 复制代码
// Component - 组件接口
public interface Beverage {
    String getDescription();
    double cost();
}

// ConcreteComponent - 具体组件
public class Espresso implements Beverage {
    @Override
    public String getDescription() {
        return "浓缩咖啡";
    }
    
    @Override
    public double cost() {
        return 1.99;
    }
}

// ConcreteComponent - 具体组件
public class HouseBlend implements Beverage {
    @Override
    public String getDescription() {
        return "综合咖啡";
    }
    
    @Override
    public double cost() {
        return 0.89;
    }
}

// Decorator - 装饰器抽象类
public abstract class CondimentDecorator implements Beverage {
    protected Beverage beverage;
    
    public CondimentDecorator(Beverage beverage) {
        this.beverage = beverage;
    }
    
    public abstract String getDescription();
}

// ConcreteDecorator - 具体装饰器
public class Mocha extends CondimentDecorator {
    public Mocha(Beverage beverage) {
        super(beverage);
    }
    
    @Override
    public String getDescription() {
        return beverage.getDescription() + ", 摩卡";
    }
    
    @Override
    public double cost() {
        return beverage.cost() + 0.20;
    }
}

// ConcreteDecorator - 具体装饰器
public class Soy extends CondimentDecorator {
    public Soy(Beverage beverage) {
        super(beverage);
    }
    
    @Override
    public String getDescription() {
        return beverage.getDescription() + ", 豆浆";
    }
    
    @Override
    public double cost() {
        return beverage.cost() + 0.15;
    }
}

// ConcreteDecorator - 具体装饰器
public class Whip extends CondimentDecorator {
    public Whip(Beverage beverage) {
        super(beverage);
    }
    
    @Override
    public String getDescription() {
        return beverage.getDescription() + ", 奶泡";
    }
    
    @Override
    public double cost() {
        return beverage.cost() + 0.10;
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        // 点一杯浓缩咖啡
        Beverage beverage = new Espresso();
        System.out.println(beverage.getDescription() + " $" + beverage.cost());
        
        // 点一杯综合咖啡,加双倍摩卡和奶泡
        Beverage beverage2 = new HouseBlend();
        beverage2 = new Mocha(beverage2);
        beverage2 = new Mocha(beverage2);
        beverage2 = new Whip(beverage2);
        System.out.println(beverage2.getDescription() + " $" + beverage2.cost());
        
        // 点一杯浓缩咖啡,加豆浆和摩卡
        Beverage beverage3 = new Espresso();
        beverage3 = new Soy(beverage3);
        beverage3 = new Mocha(beverage3);
        System.out.println(beverage3.getDescription() + " $" + beverage3.cost());
    }
}

另一个示例 - 文本处理

java 复制代码
// Component - 组件接口
public interface TextDisplay {
    String getText();
    void display();
}

// ConcreteComponent - 具体组件
public class PlainText implements TextDisplay {
    private String text;
    
    public PlainText(String text) {
        this.text = text;
    }
    
    @Override
    public String getText() {
        return text;
    }
    
    @Override
    public void display() {
        System.out.println(text);
    }
}

// Decorator - 装饰器抽象类
public abstract class TextDecorator implements TextDisplay {
    protected TextDisplay textDisplay;
    
    public TextDecorator(TextDisplay textDisplay) {
        this.textDisplay = textDisplay;
    }
    
    @Override
    public String getText() {
        return textDisplay.getText();
    }
    
    @Override
    public void display() {
        textDisplay.display();
    }
}

// ConcreteDecorator - 具体装饰器
public class BoldTextDecorator extends TextDecorator {
    public BoldTextDecorator(TextDisplay textDisplay) {
        super(textDisplay);
    }
    
    @Override
    public String getText() {
        return "**" + textDisplay.getText() + "**";
    }
    
    @Override
    public void display() {
        System.out.println("**" + textDisplay.getText() + "**");
    }
}

// ConcreteDecorator - 具体装饰器
public class ItalicTextDecorator extends TextDecorator {
    public ItalicTextDecorator(TextDisplay textDisplay) {
        super(textDisplay);
    }
    
    @Override
    public String getText() {
        return "*" + textDisplay.getText() + "*";
    }
    
    @Override
    public void display() {
        System.out.println("*" + textDisplay.getText() + "*");
    }
}

// ConcreteDecorator - 具体装饰器
public class UnderlineTextDecorator extends TextDecorator {
    public UnderlineTextDecorator(TextDisplay textDisplay) {
        super(textDisplay);
    }
    
    @Override
    public String getText() {
        return "_" + textDisplay.getText() + "_";
    }
    
    @Override
    public void display() {
        System.out.println("_" + textDisplay.getText() + "_");
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        // 创建纯文本
        TextDisplay plainText = new PlainText("Hello World");
        plainText.display();
        
        // 加粗文本
        TextDisplay boldText = new BoldTextDecorator(plainText);
        boldText.display();
        
        // 斜体文本
        TextDisplay italicText = new ItalicTextDecorator(plainText);
        italicText.display();
        
        // 加粗斜体文本
        TextDisplay boldItalicText = new BoldTextDecorator(new ItalicTextDecorator(plainText));
        boldItalicText.display();
        
        // 加粗斜体下划线文本
        TextDisplay decoratedText = new UnderlineTextDecorator(
            new BoldTextDecorator(
                new ItalicTextDecorator(plainText)
            )
        );
        decoratedText.display();
    }
}

优缺点

优点

  1. 装饰器模式与继承关系的目的都是要扩展对象的功能,但是装饰器模式可以提供比继承更多的灵活性
  2. 可以通过一种动态的方式来扩展一个对象的功能,通过配置文件可以在运行时选择不同的装饰器,从而实现不同的行为
  3. 通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合。可以使用多个具体装饰类来装饰同一个对象,得到功能更为强大的对象
  4. 具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,原有类库代码无须改变,符合"开闭原则"

缺点

  1. 装饰器模式比继承更加灵活,但也比继承更加复杂
  2. 装饰器模式会产生许多小对象,这些对象的区别在于它们之间相互连接的方式不同,而不是它们的类或是属性值不同,这将使得许多小对象变得难以理解
  3. 装饰器模式比继承更容易出错,排错也更困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为繁琐

相关模式

  • 适配器模式:适配器模式改变对象的接口,而装饰器模式不改变对象的接口,只增加职责
  • 组合模式:组合模式可以将装饰器和组件以一致的方式处理
  • 策略模式:策略模式改变对象的行为,而装饰器模式增加对象的职责
  • 外观模式:外观模式提供了一个简化的接口,而装饰器模式提供了增强的接口

实际应用

  • Java中的I/O流类
  • Swing中的组件装饰
  • Spring框架中的BeanWrapper
  • Java中的Collections.synchronizedList()方法
  • Web开发中的过滤器链

Java I/O中的装饰器模式

Java的I/O流是装饰器模式的经典应用:

java 复制代码
// 基本组件
InputStream inputStream = new FileInputStream("test.txt");

// 装饰器1 - 缓冲流
InputStream bufferedStream = new BufferedInputStream(inputStream);

// 装饰器2 - 数据流
InputStream dataStream = new DataInputStream(bufferedStream);

// 装饰器3 - 对象流
ObjectInputStream objectStream = new ObjectInputStream(dataStream);

装饰器模式与继承的区别

  • 装饰器模式:动态地添加功能,可以在运行时组合不同的装饰器
  • 继承:静态地添加功能,在编译时确定,无法在运行时改变

装饰器模式提供了比继承更大的灵活性,但同时也增加了系统的复杂性。在选择使用装饰器模式还是继承时,需要根据具体的需求来决定。

相关推荐
进击的小头2 天前
创建型模式:装饰器模式(C语言实战指南)
c语言·开发语言·装饰器模式
小码过河.3 天前
17装饰器模式
开发语言·python·装饰器模式
茶本无香3 天前
设计模式之七—装饰模式(Decorator Pattern)
java·设计模式·装饰器模式
obDLaSfLKr6 天前
Canoe-Autosar网络管理测试脚本用例CAPL 这适用于Autosar NM主流测试用...
装饰器模式
Geoking.19 天前
【设计模式】装饰者模式详解
设计模式·装饰器模式
蔺太微20 天前
装饰器模式(Decorator Pattern)
设计模式·装饰器模式
moxiaoran575321 天前
使用策略模式+装饰器模式实现接口防重复提交
java·装饰器模式
sxlishaobin21 天前
设计模式之装饰器模式
java·设计模式·装饰器模式
apolloyhl21 天前
Decorator 装饰模式
装饰器模式