设计模式-装饰者模式

文章目录

  • 一、概述
    • [1.1 结构与角色](#1.1 结构与角色)
    • [1.2 适用场景](#1.2 适用场景)
  • 二、实现方式
    • [2.1 基础实现------咖啡调味系统](#2.1 基础实现——咖啡调味系统)
    • [2.2 继承 vs 装饰者对比](#2.2 继承 vs 装饰者对比)
    • [2.3 实际应用示例------文本处理系统](#2.3 实际应用示例——文本处理系统)
  • [三、JDK 源码中的装饰者](#三、JDK 源码中的装饰者)
    • [3.1 Java I/O 体系](#3.1 Java I/O 体系)
    • [3.2 Collections.unmodifiableList](#3.2 Collections.unmodifiableList)
    • [3.3 Servlet API------HttpServletRequestWrapper](#3.3 Servlet API——HttpServletRequestWrapper)
  • [四、装饰者 vs 代理模式](#四、装饰者 vs 代理模式)
    • [4.1 意图对比](#4.1 意图对比)
    • [4.2 结构对比](#4.2 结构对比)
    • [4.3 代码对比](#4.3 代码对比)
    • [4.4 选择指南](#4.4 选择指南)
  • 五、总结

一、概述

在软件开发中,经常会遇到这样的场景:需要为一个对象动态地 添加新的功能,而不是通过继承来扩展。如果使用继承来实现,每增加一种功能组合就需要创建一个新的子类,导致类的数量呈指数级增长。例如,一杯咖啡可以加牛奶、加糖、加摩卡......如果用继承来表示每一种组合,类的数量将难以维护。

装饰者模式(Decorator Pattern)正是为了解决这个问题而诞生的------它动态地给对象增加新的功能,同时又不改变其结构。装饰者模式是替代继承的一种方案,它通过创建包装对象(即装饰者)来包裹真实对象,并在保持接口一致的前提下,增强或扩展对象的功能。

生活中的装饰者例子比比皆是:

  • 咖啡调味:一杯黑咖啡(被装饰者)可以加牛奶(装饰者A)、加糖(装饰者B)、加摩卡(装饰者C),这些调料可以自由组合,层层包裹
  • 穿衣搭配:一个人(被装饰者)可以穿T恤(装饰者A)、穿外套(装饰者B)、戴帽子(装饰者C),衣物可以逐层穿戴
  • 礼品包装:一份礼物(被装饰者)可以包一层彩纸(装饰者A)、再系一条丝带(装饰者B)、再贴一朵花(装饰者C),层层装饰

核心:动态地给对象增加新的功能,同时不改变其结构,是替代继承的一种方案

1.1 结构与角色

装饰者模式包含以下角色:
实现
实现
持有
继承
继承
调用
Client 客户端
Component 抽象构件
ConcreteComponent 具体构件
Decorator 抽象装饰者
ConcreteDecoratorA 具体装饰者A
ConcreteDecoratorB 具体装饰者B

  • Component(抽象构件):定义对象的接口,可以给这些对象动态添加功能
  • ConcreteComponent(具体构件):定义一个具体的对象,也可以给这个对象添加一些功能
  • Decorator(抽象装饰者):持有一个抽象构件的引用,并实现抽象构件的接口,通过子类扩展具体构件的功能
  • ConcreteDecorator(具体装饰者):继承抽象装饰者,负责给构件添加新的功能
  • Client(客户端):通过抽象构件接口与对象交互,无需关心对象是否被装饰

1.2 适用场景

  • 需要动态地给对象添加功能,且这些功能可以动态撤销
  • 需要组合多种功能,使用继承导致子类爆炸
  • 不能采用继承的方式对系统进行扩展,或者继承不利于系统扩展和维护时

二、实现方式

装饰者模式的核心实现思路是:定义一个抽象装饰者类实现抽象构件接口,并持有抽象构件的引用,在具体装饰者中通过委托调用被装饰者的方法,并在其前后添加新的功能。

2.1 基础实现------咖啡调味系统

以咖啡店为例,一杯咖啡(被装饰者)可以加牛奶、加糖、加摩卡等调料(装饰者),调料可以自由组合:
实现
实现
持有
继承
继承
继承
客户端
Drink 抽象饮品
Coffee 咖啡
Decorator 调料装饰者
Milk 牛奶
Sugar 糖
Mocha 摩卡

(1)抽象构件------饮品

java 复制代码
/**
 * 抽象构件:饮品
 */
public abstract class Drink {

    /**
     * 描述
     */
    protected String description = "未知饮品";

    /**
     * 获取描述
     *
     * @return 描述信息
     */
    public String getDescription() {
        return description;
    }

    /**
     * 计算价格
     *
     * @return 价格
     */
    public abstract double cost();
}

(2)具体构件------咖啡

java 复制代码
/**
 * 具体构件:咖啡
 */
public class Coffee extends Drink {

    public Coffee() {
        description = "咖啡";
    }

    @Override
    public double cost() {
        return 10.0;
    }
}

(3)抽象装饰者------调料装饰者

java 复制代码
/**
 * 抽象装饰者:调料装饰者
 * 继承 Drink 并持有 Drink 的引用
 */
public abstract class DrinkDecorator extends Drink {

    /**
     * 持有抽象构件的引用
     */
    protected Drink drink;

    public DrinkDecorator(Drink drink) {
        this.drink = drink;
    }

    /**
     * 抽象方法:获取描述,由具体装饰者实现
     */
    @Override
    public abstract String getDescription();
}

(4)具体装饰者------牛奶、糖、摩卡

java 复制代码
/**
 * 具体装饰者:牛奶
 */
public class Milk extends DrinkDecorator {

    public Milk(Drink drink) {
        super(drink);
    }

    @Override
    public String getDescription() {
        return drink.getDescription() + " + 牛奶";
    }

    @Override
    public double cost() {
        return drink.cost() + 3.0;
    }
}

/**
 * 具体装饰者:糖
 */
public class Sugar extends DrinkDecorator {

    public Sugar(Drink drink) {
        super(drink);
    }

    @Override
    public String getDescription() {
        return drink.getDescription() + " + 糖";
    }

    @Override
    public double cost() {
        return drink.cost() + 1.0;
    }
}

/**
 * 具体装饰者:摩卡
 */
public class Mocha extends DrinkDecorator {

    public Mocha(Drink drink) {
        super(drink);
    }

    @Override
    public String getDescription() {
        return drink.getDescription() + " + 摩卡";
    }

    @Override
    public double cost() {
        return drink.cost() + 5.0;
    }
}

(5)客户端调用

java 复制代码
public class CoffeeShopDemo {
    public static void main(String[] args) {
        // 1. 一杯普通咖啡
        Drink coffee = new Coffee();
        System.out.println(coffee.getDescription() + " ¥" + coffee.cost());
        // 咖啡 ¥10.0

        // 2. 咖啡 + 牛奶
        Drink milkCoffee = new Milk(coffee);
        System.out.println(milkCoffee.getDescription() + " ¥" + milkCoffee.cost());
        // 咖啡 + 牛奶 ¥13.0

        // 3. 咖啡 + 牛奶 + 糖
        Drink milkSugarCoffee = new Sugar(milkCoffee);
        System.out.println(milkSugarCoffee.getDescription() + " ¥" + milkSugarCoffee.cost());
        // 咖啡 + 牛奶 + 糖 ¥14.0

        // 4. 咖啡 + 摩卡 + 牛奶 + 糖
        Drink fullCoffee = new Mocha(milkSugarCoffee);
        System.out.println(fullCoffee.getDescription() + " ¥" + fullCoffee.cost());
        // 咖啡 + 牛奶 + 糖 + 摩卡 ¥19.0
    }
}

关键点 :每个具体装饰者都持有一个 Drink 对象的引用,通过委托调用被装饰者的方法,并在其基础上添加自己的功能(修改描述和增加价格)。装饰者可以层层嵌套,每层添加一个新功能。

2.2 继承 vs 装饰者对比

如果用继承来实现咖啡调味系统,类的数量将急剧增长:
Coffee 咖啡
MilkCoffee 牛奶咖啡
SugarCoffee 糖咖啡
MochaCoffee 摩卡咖啡
MilkSugarCoffee 牛奶糖咖啡
MilkMochaCoffee 牛奶摩卡咖啡
SugarMochaCoffee 糖摩卡咖啡
MilkSugarMochaCoffee 牛奶糖摩卡咖啡

对比维度 继承方式 装饰者模式
类的数量 (2^n - 1)(n 为调料种类) 1 + n(1 个具体构件 + n 个具体装饰者)
扩展新功能 需要创建大量子类 只需新增一个具体装饰者类
功能组合 编译时确定,静态组合 运行时动态组合
功能撤销 不支持 移除装饰者即可撤销
灵活性

对比:3 种调料用继承需要 7 个子类,而装饰者模式只需 3 个具体装饰者类 + 1 个具体构件类 = 4 个类。每新增一种调料,继承方式需要翻倍的子类,而装饰者模式只需 +1 个类。

2.3 实际应用示例------文本处理系统

以文本处理系统为例,文本可以经过多种处理器层层处理:加密、压缩、Base64 编码等,处理器可以自由组合:
实现
实现
持有
继承
继承
继承
客户端
TextProcessor 抽象文本处理器
PlainText 纯文本
ProcessorDecorator 处理器装饰者
EncryptionProcessor 加密处理器
CompressionProcessor 压缩处理器
Base64Processor Base64处理器

(1)抽象构件------文本处理器

java 复制代码
/**
 * 抽象构件:文本处理器
 */
public interface TextProcessor {

    /**
     * 处理文本
     *
     * @param text 原始文本
     * @return 处理后的文本
     */
    String process(String text);
}

(2)具体构件------纯文本

java 复制代码
/**
 * 具体构件:纯文本处理器
 */
public class PlainText implements TextProcessor {

    @Override
    public String process(String text) {
        return text;
    }
}

(3)抽象装饰者------处理器装饰者

java 复制代码
/**
 * 抽象装饰者:处理器装饰者
 */
public abstract class ProcessorDecorator implements TextProcessor {

    /**
     * 持有文本处理器的引用
     */
    protected TextProcessor processor;

    public ProcessorDecorator(TextProcessor processor) {
        this.processor = processor;
    }

    @Override
    public String process(String text) {
        return processor.process(text);
    }
}

(4)具体装饰者------加密、压缩、Base64

java 复制代码
/**
 * 具体装饰者:加密处理器
 */
public class EncryptionProcessor extends ProcessorDecorator {

    private static final int SHIFT = 3;

    public EncryptionProcessor(TextProcessor processor) {
        super(processor);
    }

    @Override
    public String process(String text) {
        String processed = super.process(text);
        return encrypt(processed);
    }

    /**
     * 简单的凯撒加密
     */
    private String encrypt(String text) {
        StringBuilder result = new StringBuilder();
        for (char c : text.toCharArray()) {
            if (Character.isLetter(c)) {
                char base = Character.isUpperCase(c) ? 'A' : 'a';
                result.append((char) ((c - base + SHIFT) % 26 + base));
            } else {
                result.append(c);
            }
        }
        return "[加密]" + result.toString();
    }
}

/**
 * 具体装饰者:压缩处理器
 */
public class CompressionProcessor extends ProcessorDecorator {

    public CompressionProcessor(TextProcessor processor) {
        super(processor);
    }

    @Override
    public String process(String text) {
        String processed = super.process(text);
        return "[压缩]" + processed.replace(" ", "");
    }
}

/**
 * 具体装饰者:Base64 处理器
 */
public class Base64Processor extends ProcessorDecorator {

    public Base64Processor(TextProcessor processor) {
        super(processor);
    }

    @Override
    public String process(String text) {
        String processed = super.process(text);
        return "[Base64]" + java.util.Base64.getEncoder().encodeToString(processed.getBytes());
    }
}

(5)客户端调用

java 复制代码
public class TextProcessingDemo {
    public static void main(String[] args) {
        String text = "Hello World";

        // 1. 纯文本
        TextProcessor plain = new PlainText();
        System.out.println("纯文本:" + plain.process(text));
        // 纯文本:Hello World

        // 2. 先压缩,再加密
        TextProcessor compressAndEncrypt = new EncryptionProcessor(new CompressionProcessor(plain));
        System.out.println("压缩+加密:" + compressAndEncrypt.process(text));
        // 压缩+加密:[加密][压缩]HelloWorld

        // 3. 先加密,再 Base64
        TextProcessor encryptAndBase64 = new Base64Processor(new EncryptionProcessor(plain));
        System.out.println("加密+Base64:" + encryptAndBase64.process(text));
        // 加密+Base64:[Base64]W2Tmt3Nd]Sbpplq...

        // 4. 先压缩,再加密,最后 Base64
        TextProcessor all = new Base64Processor(
                new EncryptionProcessor(
                        new CompressionProcessor(plain)));
        System.out.println("全部处理:" + all.process(text));
    }
}

优势体现 :处理器的执行顺序可以自由组合,只需要改变装饰者的嵌套顺序即可。如果未来需要新增"签名处理器",只需创建一个 SignatureProcessor 具体装饰者类,无需修改任何已有代码。


三、JDK 源码中的装饰者

装饰者模式在 JDK 中有着广泛的应用,最典型的就是 Java I/O 体系。

3.1 Java I/O 体系

Java 的 I/O 流是装饰者模式的经典应用。InputStream 是抽象构件,FileInputStreamByteArrayInputStream 等是具体构件,FilterInputStream 是抽象装饰者,BufferedInputStreamDataInputStream 等是具体装饰者。
实现
继承
持有
继承
继承
InputStream 抽象构件
FileInputStream 具体构件
FilterInputStream 抽象装饰者
BufferedInputStream 具体装饰者
DataInputStream 具体装饰者

(1)抽象构件------InputStream

java 复制代码
public abstract class InputStream implements Closeable {

    /**
     * 读取一个字节
     */
    public abstract int read() throws IOException;

    /**
     * 读取多个字节到数组
     */
    public int read(byte[] b) throws IOException {
        return read(b, 0, b.length);
    }

    /**
     * 读取多个字节到数组(指定偏移和长度)
     */
    public int read(byte[] b, int off, int len) throws IOException {
        // ... 默认实现
    }
}

(2)抽象装饰者------FilterInputStream

java 复制代码
public class FilterInputStream extends InputStream {

    /**
     * 持有抽象构件的引用
     */
    protected volatile InputStream in;

    protected FilterInputStream(InputStream in) {
        this.in = in;
    }

    @Override
    public int read() throws IOException {
        return in.read();
    }

    @Override
    public int read(byte[] b) throws IOException {
        return read(b, 0, b.length);
    }

    @Override
    public int read(byte[] b, int off, int len) throws IOException {
        return in.read(b, off, len);
    }

    @Override
    public void close() throws IOException {
        in.close();
    }
}

(3)具体装饰者------BufferedInputStream

java 复制代码
public class BufferedInputStream extends FilterInputStream {

    /**
     * 缓冲区大小
     */
    private static final int DEFAULT_BUFFER_SIZE = 8192;

    /**
     * 内部缓冲区
     */
    protected volatile byte[] buf;

    public BufferedInputStream(InputStream in) {
        super(in);
        buf = new byte[DEFAULT_BUFFER_SIZE];
    }

    public BufferedInputStream(InputStream in, int size) {
        super(in);
        if (size <= 0) {
            throw new IllegalArgumentException("Buffer size <= 0");
        }
        buf = new byte[size];
    }

    /**
     * 读取一个字节(带缓冲)
     * 在被装饰者基础上增加了缓冲功能
     */
    @Override
    public int read() throws IOException {
        // 委托调用父类方法,并增加缓冲功能
        // ... 缓冲逻辑
    }
}

在这个例子中:

  • Component(抽象构件)InputStream
  • ConcreteComponent(具体构件)FileInputStreamByteArrayInputStream
  • Decorator(抽象装饰者)FilterInputStream
  • ConcreteDecorator(具体装饰者)BufferedInputStreamDataInputStreamPushbackInputStream

使用方式------装饰者层层嵌套:

java 复制代码
// 基础构件:文件输入流
InputStream fileInputStream = new FileInputStream("data.txt");

// 加缓冲装饰
InputStream bufferedInputStream = new BufferedInputStream(fileInputStream);

// 加数据读取装饰
DataInputStream dataInputStream = new DataInputStream(bufferedInputStream);

// 读取数据
int value = dataInputStream.readInt();

说明BufferedInputStreamFileInputStream 的基础上增加了缓冲功能,DataInputStreamBufferedInputStream 的基础上增加了读取基本数据类型的功能。三层装饰者层层包裹,每层各司其职。

3.2 Collections.unmodifiableList

java.util.Collections 中的不可变集合方法也是装饰者模式的应用。UnmodifiableList 包装一个 List,在保持 List 接口不变的前提下,将所有修改操作替换为抛出 UnsupportedOperationException

java 复制代码
public class Collections {

    /**
     * 返回不可修改的 List 视图
     * 装饰者模式:包装原 List,禁止修改操作
     */
    @SuppressWarnings("unchecked")
    public static <T> List<T> unmodifiableList(List<? extends T> list) {
        return (list instanceof RandomAccess)
                ? new UnmodifiableRandomAccessList<>(list)
                : new UnmodifiableList<>(list);
    }
}

/**
 * 具体装饰者:不可修改的 List
 */
static class UnmodifiableList<E> extends UnmodifiableCollection<E>
        implements List<E> {

    final List<? extends E> list;

    UnmodifiableList(List<? extends E> list) {
        super(list);
        this.list = list;
    }

    // 读取操作------委托给原 List
    @Override
    public E get(int index) {
        return list.get(index);
    }

    // 修改操作------抛出异常
    @Override
    public E set(int index, E element) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void add(int index, E element) {
        throw new UnsupportedOperationException();
    }

    @Override
    public E remove(int index) {
        throw new UnsupportedOperationException();
    }
}

在这个例子中:

  • Component(抽象构件)List<E>
  • ConcreteComponent(具体构件)ArrayListLinkedList 等具体的 List
  • Decorator(具体装饰者)UnmodifiableList(限制修改操作)

使用方式:

java 复制代码
List<String> mutableList = new ArrayList<>(Arrays.asList("A", "B", "C"));

// 装饰为不可修改的 List
List<String> unmodifiable = Collections.unmodifiableList(mutableList);

// 读取------正常工作
unmodifiable.get(0);  // "A"

// 修改------抛出异常
unmodifiable.add("D"); // UnsupportedOperationException

3.3 Servlet API------HttpServletRequestWrapper

在 Servlet API 中,HttpServletRequestWrapper 是装饰者模式的典型应用。它实现了 HttpServletRequest 接口,并持有一个 HttpServletRequest 引用,所有方法默认委托给原对象。开发者可以继承 HttpServletRequestWrapper,只需重写需要修改的方法。

java 复制代码
/**
 * 抽象装饰者:HttpServletRequestWrapper
 * 所有方法默认委托给原 HttpServletRequest
 */
public class HttpServletRequestWrapper implements HttpServletRequest {

    private final HttpServletRequest request;

    public HttpServletRequestWrapper(HttpServletRequest request) {
        this.request = request;
    }

    @Override
    public String getParameter(String name) {
        return request.getParameter(name);
    }

    @Override
    public String getHeader(String name) {
        return request.getHeader(name);
    }

    // ... 其他方法均委托给 request
}

开发者可以继承 HttpServletRequestWrapper,重写特定方法来实现自定义逻辑:

java 复制代码
/**
 * 具体装饰者:XSS 防护请求包装器
 * 重写 getParameter 方法,过滤危险字符
 */
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {

    public XssHttpServletRequestWrapper(HttpServletRequest request) {
        super(request);
    }

    @Override
    public String getParameter(String name) {
        String value = super.getParameter(name);
        if (value != null) {
            // 过滤 XSS 危险字符
            value = value.replace("<", "&lt;")
                         .replace(">", "&gt;")
                         .replace("\"", "&quot;")
                         .replace("'", "&#x27;");
        }
        return value;
    }
}

说明HttpServletRequestWrapper 让开发者只需关注需要修改的方法,而不需要实现 HttpServletRequest 的所有方法。这是装饰者模式简化扩展的典型体现。


四、装饰者 vs 代理模式

装饰者模式和代理模式都属于结构型模式,而且结构非常相似------都持有目标对象的引用,都实现了相同的接口,都通过委托来调用目标对象的方法。但它们的意图使用场景截然不同。

关于代理模式可参见博客:设计模式-代理模式

4.1 意图对比

对比维度 装饰者模式 代理模式
核心意图 动态地给对象增加功能 控制对对象的访问
解决的问题 功能组合导致的子类爆炸 访问控制、延迟加载、远程访问
对象关系 装饰者与被装饰者是平等的,可以层层嵌套 代理与被代理者是替代关系,客户端不直接访问被代理者
对象数量 可以有多个装饰者同时装饰一个对象 通常一个代理对应一个被代理者
对象创建 由客户端动态组合 代理对象可能自行创建被代理者
接口一致 两者都实现相同的接口 两者都实现相同的接口

4.2 结构对比

代理模式
持有
Subject
Proxy
RealSubject
装饰者模式
持有
Component
Decorator
ConcreteDecorator

装饰者模式:Decorator 持有 Component 的引用,与 Component 是平等的,客户端知道被装饰者是谁,装饰者由客户端动态组合。

代理模式:Proxy 持有 RealSubject 的引用,Proxy 是 RealSubject 的替代品,客户端通常不知道 RealSubject 的存在,Proxy 控制对 RealSubject 的访问。

4.3 代码对比

以"图片加载"为例,对比两种模式的应用方式:

装饰者模式的用法------给图片加载增加额外功能:

java 复制代码
// 抽象构件
public interface Image {
    void display();
}

// 具体构件
public class SimpleImage implements Image {
    @Override
    public void display() {
        System.out.println("显示图片");
    }
}

// 装饰者:添加边框功能
public class BorderDecorator implements Image {

    private final Image image;

    public BorderDecorator(Image image) {
        this.image = image;
    }

    @Override
    public void display() {
        System.out.println("添加边框");
        image.display();
    }
}

// 装饰者:添加滤镜功能
public class FilterDecorator implements Image {

    private final Image image;

    public FilterDecorator(Image image) {
        this.image = image;
    }

    @Override
    public void display() {
        System.out.println("添加滤镜");
        image.display();
    }
}

// 客户端:自由组合装饰者
Image image = new FilterDecorator(new BorderDecorator(new SimpleImage()));
image.display();
// 添加滤镜
// 添加边框
// 显示图片

代理模式的用法------控制图片的延迟加载:

java 复制代码
// 抽象主题
public interface Image {
    void display();
}

// 真实主题
public class HighResolutionImage implements Image {

    private final String filename;

    public HighResolutionImage(String filename) {
        // 加载大文件------耗时操作
        System.out.println("从磁盘加载高清图片:" + filename);
        this.filename = filename;
    }

    @Override
    public void display() {
        System.out.println("显示高清图片:" + filename);
    }
}

// 代理:延迟加载
public class ImageProxy implements Image {

    private final String filename;
    private HighResolutionImage realImage;

    public ImageProxy(String filename) {
        this.filename = filename;
    }

    @Override
    public void display() {
        // 延迟加载------只在真正需要时才创建真实对象
        if (realImage == null) {
            realImage = new HighResolutionImage(filename);
        }
        realImage.display();
    }
}

// 客户端:通过代理访问
Image image = new ImageProxy("photo.jpg");
image.display(); // 第一次调用时才加载

关键区别 :装饰者模式关注的是功能增强 ,客户端主动组合装饰者来添加功能;代理模式关注的是访问控制,代理对象代替真实对象控制访问,客户端不需要知道真实对象的存在。

4.4 选择指南

场景 选择
需要动态组合多种功能 装饰者模式
需要在不修改已有代码的前提下扩展功能 装饰者模式
功能需要层层叠加,且可以自由组合 装饰者模式
需要控制对象的访问权限 代理模式
需要延迟加载(懒加载) 代理模式
需要访问远程对象 代理模式
需要在不修改源码的前提下增加缓存功能 代理模式

五、总结

装饰者模式的核心思想是动态地给对象增加新的功能,通过创建包装对象来包裹真实对象,在不改变其结构的前提下扩展功能,是替代继承的一种方案。

优点:

  • 动态扩展:可以在运行时动态地给对象添加功能,比继承更灵活
  • 避免子类爆炸:用组合代替继承,功能组合不需要创建大量子类
  • 符合开闭原则:新增功能只需新增具体装饰者类,无需修改已有代码
  • 功能可撤销:移除装饰者即可撤销对应的功能
  • 功能可组合:多个装饰者可以自由组合,顺序灵活

缺点:

  • 增加系统复杂度:多了一层装饰者,增加了类的数量
  • 调试困难:多层装饰者嵌套后,调试时不容易理清调用关系
  • 装饰者顺序敏感:不同的装饰顺序可能产生不同的结果
  • 特殊类型问题:装饰者包装后,无法通过 instanceof 判断是否为特定具体构件

适用场景:

  • 需要动态地给对象添加功能,且可以动态撤销
  • 需要组合多种功能,使用继承导致子类爆炸
  • 不能通过继承来扩展系统(如 final 类),或继承不利于系统维护
  • 需要在不修改已有代码的前提下扩展对象的功能

装饰者 vs 代理 :装饰者模式的意图是功能增强,客户端主动组合装饰者来添加功能;代理模式的意图是控制访问,代理对象代替真实对象控制访问,客户端通常不直接接触真实对象。两者结构相似,但意图截然不同。


参考博客:

装饰器模式 | 菜鸟教程:https://www.runoob.com/design-pattern/decorator-pattern.html

相关推荐
庞轩px16 小时前
第六篇:Spring用了哪些设计模式?——从单例到代理,拆解框架中的经典设计
java·spring·设计模式·bean·代理模式·aop·单例
多加点辣也没关系16 小时前
设计模式-工厂方法模式
设计模式·工厂方法模式
多加点辣也没关系21 小时前
设计模式-建造者模式
设计模式·建造者模式
多加点辣也没关系1 天前
设计模式-桥接模式
设计模式·桥接模式
雪度娃娃1 天前
结构型设计模式——装饰模式
设计模式·装饰器模式
sensen_kiss1 天前
CPT304 SoftwareEngineeringII 软件工程 2 Pt.4 设计模式(下)
设计模式·软件工程
多加点辣也没关系1 天前
设计模式-适配器模式
设计模式
Forget the Dream1 天前
基于适配器模式的 Axios 封装实践
设计模式·typescript·axios·适配器模式
Java面试题总结1 天前
【设计模式03】使用模版模式+责任链模式优化实战
设计模式·责任链模式