【软考设计模式】装饰模式:“套娃“式增强与动态职责扩展精讲

系列定位:软考软件设计师 / 系统架构设计师 --- 结构型模式专题第 3 讲

考察分值:上午题 1-2 分,下午题常作为代码填空或类图识别出现

难度等级:⭐⭐⭐☆☆(结构像套娃,层层包装,掌握递归组合后很稳)


一、考纲定位与模式定义

1.1 考纲要求

装饰模式在软考中属于 结构型模式 的基础内容。考察形式包括:

  • 上午选择题:判断场景描述所属模式;识别装饰模式与桥接模式、适配器模式的区别;判断类图中是否存在 "递归组合" 结构(Decorator 组合 Component)

  • 下午设计题:补全 Decorator 抽象类的构造器(注入 Component)和委托逻辑;补全 ConcreteDecorator 中新增的职责方法;识别类图中 Component、ConcreteComponent、Decorator、ConcreteDecorator 四个角色

1.2 模式定义

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

核心意图 :当需要 在不改变原有对象接口的前提下 ,给对象 动态添加新功能 时,不用继承(继承是静态的、编译时确定的),而是用 包装(Wrapper) 的方式,在运行时层层套娃。每一层装饰者都持有被装饰对象的引用,在调用被装饰对象方法的前后插入自己的增强逻辑。

通俗理解

你去奶茶店点一杯奶茶。基础款是 "珍珠奶茶"(ConcreteComponent),你可以加料:+ 椰果(ConcreteDecoratorA)、+ 布丁(ConcreteDecoratorB)、+ 奶盖(ConcreteDecoratorC)。每加一种料,都是在原有奶茶的基础上 包装一层,最终你拿到的是一杯 "奶盖 + 布丁 + 椰果 + 珍珠奶茶"。每一层都不改变奶茶的 "喝" 这个接口,但增加了新的口感(职责)。


二、UML 类图与角色划分

复制代码
┌────────────────────────────────────────────────────────────┐
│                      Client                                │
│  + main()                                                  │
│    ├── Component c = new ConcreteComponent();              │
│    ├── c = new ConcreteDecoratorA(c);  // 第一层包装        │
│    ├── c = new ConcreteDecoratorB(c);  // 第二层包装        │
│    └── c.operation();  // 逐层调用,最终到 ConcreteComponent │
└─────────────────────────────┬──────────────────────────────┘
                              │ uses
                              ▼
┌────────────────────────────────────────────────────────────┐
│                  <<interface>>                             │
│                   Component                                │
│  + operation()                                             │
└─────────────────────────────┬──────────────────────────────┘
                 ┌────────────┴────────────┐
                 ▼                         ▼
┌────────────────────────┐   ┌────────────────────────┐
│ ConcreteComponent      │   │    Decorator           │
│  (被装饰的原始对象)       │   │  - component:          │
│  + operation()         │   │    Component           │
│    (原始业务逻辑)        │   │  + operation() {       │
│                        │   │      // 可前置增强       │
│                        │   │      component.        │
│                        │   │        operation();    │
│                        │   │      // 可后置增强       │
│                        │   │    }                   │
└────────────────────────┘   └──────────┬─────────────┘
                                        │
                     ┌──────────────────┴──────────────────┐
                     ▼                                     ▼
         ┌─────────────────────┐           ┌─────────────────────┐
         │ ConcreteDecoratorA  │           │ ConcreteDecoratorB  │
         │  + operation() {    │           │  + operation() {    │
         │      // 增强A        │           │      // 增强B       │
         │      super.op();    │           │      super.op();    │
         │      // 增强A        │           │      // 增强B       │
         │    }                │           │    }                │
         │  + addedBehavior()  │           │  + addedBehavior()  │
         └─────────────────────┘           └─────────────────────┘
角色 职责 软考填空关键词
Component(抽象组件) 定义一个对象接口,规范所有对象(被装饰者和装饰者)的统一接口 interface / abstract class + operation()
ConcreteComponent(具体组件) 被装饰的原始对象,实现 Component 接口,完成核心功能 原始业务类,如 CoffeeFileReader
Decorator(抽象装饰者) 实现 Component 接口,组合 一个 Component 引用,将请求委托给被装饰对象 abstract class + protected Component component + 构造器注入
ConcreteDecorator(具体装饰者) 继承 Decorator,在 operation() 中调用父类委托方法的前后添加自己的增强逻辑 extends Decorator + super.operation() + 新增职责方法
Client(客户端) 创建 ConcreteComponent,并用 ConcreteDecorator 层层包装,最终调用 operation() new DecoratorA(new DecoratorB(new Component()))

类图识别要点 :软考类图中,装饰模式的核心特征是 Decorator 类与 Component 是组合关系(Decorator 持有 Component 引用),且 Decorator 与 Component 实现同一个接口(或继承同一个抽象类)。如果类图里只有一个转换类连接两个不同接口 → 适配器;如果有两个独立继承树 → 桥接;如果是一个递归包装结构 → 装饰。


三、场景一:咖啡加料(最经典的装饰模式)

业务背景:奶茶店的基础饮品是 "珍珠奶茶"(ConcreteComponent),顾客可以加料:椰果、布丁、奶盖。每加一种料,价格增加,描述也变化。要求在不改变奶茶类的前提下,动态添加配料。

说明:这是 GoF 原书中装饰模式的经典例子,也是软考下午题最爱考的 "层层包装增强" 场景。

3.1 代码实现
java 复制代码
// Component:饮品接口
public interface Beverage {
    String getDescription();
    double getCost();
}

// ConcreteComponent:珍珠奶茶(原始对象)
public class PearlMilkTea implements Beverage {
    @Override
    public String getDescription() {
        return "珍珠奶茶";
    }
    
    @Override
    public double getCost() {
        return 12.0;
    }
}

// Decorator:抽象装饰者(所有加料的父类)
public abstract class CondimentDecorator implements Beverage {
    protected Beverage beverage;
    
    public CondimentDecorator(Beverage beverage) {
        this.beverage = beverage;
    }
}

// ConcreteDecorator:椰果加料
public class Coconut extends CondimentDecorator {
    public Coconut(Beverage beverage) {
        super(beverage);
    }
    
    @Override
    public String getDescription() {
        return beverage.getDescription() + " + 椰果";
    }
    
    @Override
    public double getCost() {
        return beverage.getCost() + 2.0;
    }
}

// ConcreteDecorator:布丁加料
public class Pudding extends CondimentDecorator {
    public Pudding(Beverage beverage) {
        super(beverage);
    }
    
    @Override
    public String getDescription() {
        return beverage.getDescription() + " + 布丁";
    }
    
    @Override
    public double getCost() {
        return beverage.getCost() + 3.0;
    }
}

// ConcreteDecorator:奶盖加料
public class CheeseFoam extends CondimentDecorator {
    public CheeseFoam(Beverage beverage) {
        super(beverage);
    }
    
    @Override
    public String getDescription() {
        return beverage.getDescription() + " + 奶盖";
    }
    
    @Override
    public double getCost() {
        return beverage.getCost() + 5.0;
    }
    
    public String getTaste() {
        return "咸香芝士味";
    }
}

// Client:客户端层层包装
public class Client {
    public static void main(String[] args) {
        Beverage beverage = new PearlMilkTea();
        System.out.println(beverage.getDescription() + " ¥" + beverage.getCost());
        
        beverage = new Coconut(beverage);
        System.out.println(beverage.getDescription() + " ¥" + beverage.getCost());
        
        beverage = new Pudding(beverage);
        System.out.println(beverage.getDescription() + " ¥" + beverage.getCost());
        
        beverage = new CheeseFoam(beverage);
        System.out.println(beverage.getDescription() + " ¥" + beverage.getCost());
        
        Beverage combo = new CheeseFoam(new Pudding(new Coconut(new PearlMilkTea())));
        System.out.println(combo.getDescription() + " ¥" + combo.getCost());
    }
}

关键体会 :每一层装饰者都持有内层对象的引用(beverage),在 getDescription()getCost() 中先调用内层对象的方法,再追加自己的逻辑。最终调用像剥洋葱一样,从外层逐层向内传递到 PearlMilkTea


四、场景二:日志装饰(实际开发场景,前置/后置增强)

业务背景 :系统有一个基础的日志记录器 SimpleLogger(ConcreteComponent),只负责写日志到文件。现在需要 动态添加 以下功能:① 给每条日志加时间戳 ② 给每条日志加 IP 地址 ③ 给每条日志加线程名。这些增强功能可以任意组合,且不能修改 SimpleLogger 的源码。

说明:这是实际开发中 AOP(面向切面编程)思想的简单实现,也是软考下午题中常见的 "在方法前后插入增强逻辑" 场景。

4.1 代码实现
java 复制代码
// Component:日志接口
public interface Logger {
    void log(String message);
}

// ConcreteComponent:基础日志记录器
public class SimpleLogger implements Logger {
    @Override
    public void log(String message) {
        System.out.println("[LOG] " + message);
    }
}

// Decorator:抽象日志装饰者
public abstract class LoggerDecorator implements Logger {
    protected Logger logger;
    
    public LoggerDecorator(Logger logger) {
        this.logger = logger;
    }
}

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

// ConcreteDecorator:IP 地址装饰
public class IpLogger extends LoggerDecorator {
    private String ip;
    
    public IpLogger(Logger logger, String ip) {
        super(logger);
        this.ip = ip;
    }
    
    @Override
    public void log(String message) {
        logger.log("[IP:" + ip + "] " + message);
    }
}

// ConcreteDecorator:线程名装饰
public class ThreadLogger extends LoggerDecorator {
    public ThreadLogger(Logger logger) {
        super(logger);
    }
    
    @Override
    public void log(String message) {
        String threadName = Thread.currentThread().getName();
        logger.log("[Thread:" + threadName + "] " + message);
    }
}

// Client:客户端自由组合
public class Client {
    public static void main(String[] args) {
        Logger logger = new SimpleLogger();
        logger.log("用户登录成功");
        
        Logger logger2 = new TimestampLogger(new SimpleLogger());
        logger2.log("用户登录成功");
        
        Logger logger3 = new ThreadLogger(
            new IpLogger(
                new TimestampLogger(new SimpleLogger()),
                "192.168.1.100"
            )
        );
        logger3.log("用户登录成功");
    }
}

关键体会 :装饰者可以在被装饰对象方法调用的 前后 插入增强逻辑(本例是前置增强)。客户端可以自由组合装饰顺序:A(B(C(component)))B(A(C(component))) 的效果可能不同,取决于各装饰者的逻辑。


五、场景三:Java IO 风格(BufferedReader 包装 FileReader)

业务背景 :Java 标准库中的 IO 流就是装饰模式的经典实现。FileReader(ConcreteComponent)提供基础的文件读取功能,BufferedReader(ConcreteDecorator)在 FileReader 的基础上增加了缓冲功能,LineNumberReader(ConcreteDecorator)又增加了行号追踪功能。使用装饰模式模拟这个设计。

说明:这是软考上午题中常考的 "识别 Java IO 中的设计模式" 场景。Reader 是 Component,FileReader 是 ConcreteComponent,BufferedReader 是 Decorator。

5.1 代码实现
java 复制代码
// Component:数据读取接口
public interface DataReader {
    String read();
}

// ConcreteComponent:文件读取器(基础功能)
public class FileReader implements DataReader {
    private String filename;
    
    public FileReader(String filename) {
        this.filename = filename;
    }
    
    @Override
    public String read() {
        return "文件 " + filename + " 的原始内容";
    }
}

// Decorator:抽象读取装饰者
public abstract class ReaderDecorator implements DataReader {
    protected DataReader reader;
    
    public ReaderDecorator(DataReader reader) {
        this.reader = reader;
    }
}

// ConcreteDecorator:缓冲装饰(增加缓冲功能)
public class BufferedReader extends ReaderDecorator {
    private String buffer;
    
    public BufferedReader(DataReader reader) {
        super(reader);
    }
    
    @Override
    public String read() {
        if (buffer == null) {
            buffer = reader.read();
            System.out.println("[BufferedReader] 已缓冲数据");
        }
        return buffer;
    }
    
    public String readLine() {
        return read() + "(按行读取)";
    }
}

// ConcreteDecorator:行号追踪装饰
public class LineNumberReader extends ReaderDecorator {
    private int lineNumber = 0;
    
    public LineNumberReader(DataReader reader) {
        super(reader);
    }
    
    @Override
    public String read() {
        lineNumber++;
        String content = reader.read();
        return "[" + lineNumber + "] " + content;
    }
    
    public int getLineNumber() {
        return lineNumber;
    }
}

// Client
public class Client {
    public static void main(String[] args) {
        DataReader reader = new FileReader("report.txt");
        System.out.println(reader.read());
        
        DataReader buffered = new BufferedReader(new FileReader("report.txt"));
        System.out.println(buffered.read());
        System.out.println(buffered.read());
        
        DataReader lineReader = new LineNumberReader(
            new BufferedReader(new FileReader("report.txt"))
        );
        System.out.println(lineReader.read());
        System.out.println(lineReader.read());
    }
}

六、三种场景对比与演进思路

维度 场景一:咖啡加料 场景二:日志装饰 场景三:Java IO
Component Beverage Logger DataReader
ConcreteComponent PearlMilkTea SimpleLogger FileReader
Decorator CondimentDecorator LoggerDecorator ReaderDecorator
ConcreteDecorator CoconutPuddingCheeseFoam TimestampLoggerIpLoggerThreadLogger BufferedReaderLineNumberReader
增强方式 累加描述和价格 前置添加元信息 前置缓冲/行号
新增方法 getTaste()(奶盖特有) readLine()getLineNumber()
包装层数 任意多层 任意多层 任意多层
软考考法 下午大题(补全价格累加) 下午大题(补全前置增强) 上午选择题(识别 Java IO)

七、装饰模式 vs 桥接模式 vs 适配器模式:核心对比

对比项 装饰模式 桥接模式 适配器模式
设计目的 动态增强 对象功能,不改变接口 分离 抽象与实现,独立变化 转换 接口,兼容不兼容的类
是否改变接口 ❌ 不改变(保持 Component 接口) ❌ 不改变(各自定义接口) ✅ 改变(Adapter 转接口)
是否新增功能 ✅ 新增功能 ❌ 不新增(只是分离) ❌ 不新增(只是转换)
类图结构 递归组合(Decorator 组合 Component) 两个独立继承树 一个 Adapter 转换类
关系类型 组合(Decorator 持有 Component) 组合(Abstraction 持有 Implementor) 组合/继承(Adapter 持有/继承 Adaptee)
Client 调用 new Decorator(new Component()) new Abstraction(new Implementor()) new Adapter(new Adaptee())
运行时特性 可动态叠加多层 创建时绑定实现层 创建时绑定被适配者
软考判断 递归包装 + 接口相同 + 增强功能 两个继承树 + 组合 一个转换类 + 接口不同

记忆口诀:装饰是 "套娃增强"(层层包装,功能叠加),桥接是 "拆桥分流"(两个维度独立),适配器是 "搭桥翻译"(接口转换兼容)。


八、软考高频考点与易混淆辨析

8.1 高频考点
考点 内容
模式分类 结构型模式(GoF 23 正式成员)
核心四角色 Component(抽象组件)、ConcreteComponent(具体组件)、Decorator(抽象装饰者)、ConcreteDecorator(具体装饰者)
核心思想 递归组合:Decorator 组合 Component,且两者实现同一接口
装饰者构造器 必须接收一个 Component 参数(public Decorator(Component c)),用于注入被装饰对象
委托逻辑 Decorator 的 operation() 中调用 component.operation(),前后插入增强逻辑
与继承的区别 装饰是 运行时 动态增强,继承是 编译时 静态绑定;装饰更灵活
适用场景 ① 需要在不影响其他对象的情况下,动态、透明地给单个对象添加职责 ② 需要撤销职责时 ③ 不能用子类扩展时
Java IO 例子 BufferedReader 装饰 FileReaderDataInputStream 装饰 InputStream
8.2 易混淆辨析:装饰 vs 代理模式
对比项 装饰模式 代理模式
目的 增强 对象功能(添加新职责) 控制 对象访问(权限、延迟加载、远程调用)
对客户端透明性 通常 不透明(客户端知道自己在用装饰者) 通常 透明(客户端以为在调用真实对象)
是否新增接口 可以新增方法(如 readLine() 一般不新增方法,保持与真实对象一致
典型例子 咖啡加料、Java IO 流 远程代理、虚拟代理、保护代理
软考判断 看到 "添加职责"、"增强功能" → 装饰 看到 "控制访问"、"延迟加载"、"权限检查" → 代理

关键区分 :装饰是为了 "加功能",代理是为了 "控访问"。如果类图里 Decorator 新增了 Component 没有的方法 → 装饰;如果 Proxy 完全保持原接口,只是控制调用时机 → 代理。


九、真题风格模拟与代码填空

模拟题 1(上午选择题)

以下关于装饰模式的叙述中,正确的是()。

A. 装饰模式属于创建型模式,主要用于创建具有增强功能的对象

B. 在装饰模式中,Decorator 通过继承 Component 来获得被装饰对象的功能

C. 装饰模式可以在不改变对象接口的前提下,动态地给对象添加额外职责

D. 装饰模式与适配器模式的核心区别在于:装饰模式转换接口,适配器模式增强功能

答案:C

解析

  • A 错误:装饰属于 结构型 模式,不是创建型。

  • B 错误:Decorator 通过 组合(持有 Component 引用)获得被装饰对象的功能,不是继承。如果用继承,就无法在运行时动态替换被装饰对象。

  • C 正确:这是装饰模式的核心定义。

  • D 错误:说反了。装饰模式 增强功能 (不改变接口),适配器模式 转换接口(不改变功能)。


模拟题 2(下午代码填空 --- 补全装饰者构造器和委托逻辑)

某系统使用装饰模式实现数据加密传输。基础组件 DataSender 负责发送明文,EncryptionDecorator 在发送前对数据进行加密。请补全(1)~(4)。

java 复制代码
// Component:数据发送接口
interface DataSender {
    void send(String data);
}

// ConcreteComponent:基础发送器(发送明文)
class PlainSender implements DataSender {
    public void send(String data) {
        System.out.println("发送数据: " + data);
    }
}

// Decorator:抽象装饰者
abstract class SenderDecorator implements DataSender {
    protected (1)______ sender;
    
    public SenderDecorator((2)______ sender) {
        this.sender = sender;
    }
}

// ConcreteDecorator:加密装饰者
class EncryptionDecorator extends SenderDecorator {
    public EncryptionDecorator(DataSender sender) {
        (3)______;
    }
    
    @Override
    public void send(String data) {
        String encrypted = "ENC(" + data + ")";
        System.out.println("[加密] 原文: " + data + " -> 密文: " + encrypted);
        (4)______;
    }
}

public class Client {
    public static void main(String[] args) {
        DataSender sender = new EncryptionDecorator(new PlainSender());
        sender.send("敏感信息");
    }
}

答案

  • (1) DataSender

  • (2) DataSender

  • (3) super(sender)

  • (4) sender.send(encrypted)

阅卷要点

  • (1)(2) 必须是 DataSender(Component 接口类型),体现面向接口编程。不能写 PlainSender 等具体类。

  • (3) 必须是 super(sender),调用父类 Decorator 的构造器完成注入。如果写 this.sender = sender 虽然功能正确,但不符合装饰模式的规范写法(应该由父类统一持有引用),软考阅卷时可能扣分。

  • (4) 必须是 sender.send(encrypted),这是将增强后的数据委托给被装饰者发送。如果写 super.sender.send(encrypted) 也算对,但 sender.send(encrypted) 更简洁。


模拟题 3(下午代码填空 --- 多层装饰与新增方法)

某系统使用装饰模式实现图片处理。基础组件 Image 提供 display() 方法。BorderDecorator 增加边框显示,FilterDecorator 增加滤镜效果。FilterDecorator 还新增了 getFilterName() 方法。请补全(1)~(3)。

java 复制代码
// Component
interface Image {
    void display();
}

// ConcreteComponent
class Photo implements Image {
    public void display() {
        System.out.println("显示照片");
    }
}

// Decorator
abstract class ImageDecorator implements Image {
    protected Image image;
    public ImageDecorator(Image image) { this.image = image; }
}

// ConcreteDecorator:边框装饰
class BorderDecorator extends ImageDecorator {
    public BorderDecorator(Image image) { super(image); }
    
    public void display() {
        System.out.println("绘制边框");
        (1)______;
    }
}

// ConcreteDecorator:滤镜装饰
class FilterDecorator extends ImageDecorator {
    private String filterName;
    
    public FilterDecorator(Image image, String filterName) {
        super(image);
        this.filterName = filterName;
    }
    
    public void display() {
        System.out.println("应用滤镜: " + filterName);
        (2)______;
    }
    
    public String getFilterName() {
        return (3)______;
    }
}

public class Client {
    public static void main(String[] args) {
        Image image = new FilterDecorator(
            new BorderDecorator(new Photo()), "黑白"
        );
        image.display();
    }
}

答案

  • (1) image.display()

  • (2) image.display()

  • (3) filterName

阅卷要点

  • (1)(2) 必须是 image.display(),这是 Decorator 将请求委托给被装饰的 Component 的标准写法。如果写 super.image.display() 也算对,但通常直接写 image.display()

  • 注意:如果 (1) 写成 super.display()零分 !因为 ImageDecorator 没有实现 display() 方法(抽象类),调用 super.display() 会编译错误。

  • (3) 必须是 filterName,返回装饰者特有的属性。


十、常见陷阱与注意事项

陷阱 1:误认为装饰是创建型模式

装饰属于 结构型 模式,因为它解决的是 对象之间的结构关系 (递归组合),而不是对象的创建问题。软考上午题如果问"以下属于创建型模式的是",选项里出现"装饰" 不能选

陷阱 2:Decorator 继承 Component 而不是组合

这是软考 最高频的失分点 。装饰模式的核心是 组合代替继承

  • ❌ 错误:class Decorator extends Component(继承,静态绑定,无法动态替换)

  • ✅ 正确:class Decorator { protected Component component; }(组合,运行时注入)

软考代码填空里,Decorator 必须持有 protected Component xxx 字段,并通过构造器注入。如果 Decorator 直接继承 ConcreteComponent,那就是错误的继承滥用。

陷阱 3:Decorator 的构造器没有注入 Component

装饰者的构造器 必须 接收一个 Component 参数,用于注入被装饰对象。如果构造器是无参的(如 new Decorator()),那就无法持有内层对象,失去了装饰的意义。软考代码填空里,如果 Decorator 构造器参数为空,通常直接判错。

陷阱 4:与适配器模式混淆
装饰模式 适配器模式
接口 不变(Component 接口) 接口 改变(Target → Adaptee 转换)
目的是 增强功能 目的是 兼容接口
可以新增方法 一般不新增方法

如果类图里 Component 和 Decorator 的方法名完全一致,只是 Decorator 在前后加了增强逻辑 → 装饰。如果 Adapter 的方法名与 Adaptee 不同 → 适配器。

陷阱 5:与代理模式混淆

装饰和代理的类图结构几乎一样(都实现了同一接口,都组合了目标对象)。区别在于 意图

  • 装饰 :为了 添加新功能(如加料、加时间戳、加缓冲)。

  • 代理 :为了 控制访问(如延迟加载、权限检查、远程调用)。

软考中如果题干强调 "增加功能"、"增强"、"添加职责" → 装饰;强调 "控制访问"、"代理"、"延迟加载" → 代理。

陷阱 6:多层装饰时的顺序问题

多层装饰的顺序可能影响最终效果。例如 new A(new B(c))new B(new A(c)) 的结果可能不同。软考代码填空通常不涉及顺序问题,但选择题可能问 "以下哪种包装顺序能得到 XXX 效果",需要仔细分析每一层装饰者的前后置逻辑。

陷阱 7:ConcreteDecorator 忘记调用 super.operation()

如果 ConcreteDecorator 的 operation() 中只写了自己的增强逻辑,忘记了调用 component.operation()(或 super.operation()),那么被装饰对象的原始功能就丢失了。软考阅卷时,如果 operation() 方法里没有委托调用,通常会扣分。


十一、总结

要点 内容
定义 动态地给一个对象添加一些额外的职责,就增加功能来说,比生成子类更为灵活
分类 结构型模式(GoF 23 正式成员)
核心四角色 Component(抽象组件)、ConcreteComponent(具体组件)、Decorator(抽象装饰者)、ConcreteDecorator(具体装饰者)
核心思想 递归组合:Decorator 组合 Component,且两者实现同一接口,运行时层层包装
装饰者构造器 必须接收 Component 参数,注入被装饰对象
委托逻辑 operation() 中调用 component.operation(),前后插入增强逻辑
与继承的区别 装饰是运行时动态增强,继承是编译时静态绑定;装饰更灵活
与适配器区别 装饰:增强功能,不改变接口;适配器:转换接口,不改变功能
与代理区别 装饰:添加新功能;代理:控制对象访问
Java 典型例子 BufferedReader 装饰 FileReaderDataInputStream 装饰 InputStream
软考重点 代码填空(补全 Decorator 构造器 + 委托调用);类图识别(递归组合结构);与适配器/代理的辨析
答题技巧 看到 "动态添加职责"、"层层包装"、"不改变接口" → 装饰;看到 new Decorator(new Component()) 的套娃写法 → 确认装饰

系列预告 :下一篇将讲 外观模式 ------ 当子系统接口过于复杂、客户端调用成本过高时,如何用 "门面" 类将复杂子系统的接口统一封装,提供一个简洁的高层接口。咱们下回见。

相关推荐
烬羽2 小时前
MCP 多服务器架构实战:一个 Agent 同时操控地图、浏览器和文件
设计模式·设计
儒雅的名5 小时前
GoF设计模式——策略模式
设计模式·bash·策略模式
折哥的程序人生 · 物流技术专研14 小时前
第4篇:Lambda 简化策略模式(Java 8+)
java·设计模式·策略模式·函数式编程·lambda·代码简化·扩充系列
jsonbro21 小时前
手把手带你实现发布订阅模式
前端·vue.js·设计模式
workflower1 天前
室内外配送机器人-应用路径
人工智能·机器学习·设计模式·矩阵·自动化
ttod_qzstudio1 天前
【软考设计模式】原型模式:“克隆“对象与深拷贝、浅拷贝精讲
设计模式·原型模式
geovindu1 天前
java: Builder Pattern
java·开发语言·后端·设计模式·建造者模式·生成器模式
折哥的程序人生 · 物流技术专研1 天前
第5篇:框架里的模板方法:HttpServlet、JdbcTemplate、AbstractList
设计模式·源码分析·jdbctemplate·httpservlet·模版方法模式·扩充系列
researcher-Jiang2 天前
Design Patterns——Template Method入门到情景实战
python·设计模式·模板方法模式