系列定位:软考软件设计师 / 系统架构设计师 --- 结构型模式专题第 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 接口,完成核心功能 | 原始业务类,如 Coffee、FileReader |
| 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 | Coconut、Pudding、CheeseFoam |
TimestampLogger、IpLogger、ThreadLogger |
BufferedReader、LineNumberReader |
| 增强方式 | 累加描述和价格 | 前置添加元信息 | 前置缓冲/行号 |
| 新增方法 | 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 装饰 FileReader,DataInputStream 装饰 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 装饰 FileReader、DataInputStream 装饰 InputStream |
| 软考重点 | 代码填空(补全 Decorator 构造器 + 委托调用);类图识别(递归组合结构);与适配器/代理的辨析 |
| 答题技巧 | 看到 "动态添加职责"、"层层包装"、"不改变接口" → 装饰;看到 new Decorator(new Component()) 的套娃写法 → 确认装饰 |
系列预告 :下一篇将讲 外观模式 ------ 当子系统接口过于复杂、客户端调用成本过高时,如何用 "门面" 类将复杂子系统的接口统一封装,提供一个简洁的高层接口。咱们下回见。