← 返回题目列表

代理模式和装饰器模式有什么区别?

高频 中等 第 5 / 25 题 更新于 2026/07/28
代理模式装饰器模式结构型模式设计模式对比

简化版

代理模式关注“控制访问”,装饰器模式关注“增强能力”。两者结构上都可能包装一个对象,但代理通常由代理对象决定是否、何时、如何访问目标对象;装饰器通常用于给对象动态叠加功能。

详细版

代理和装饰器都属于结构型模式,也都常见“包装一个同接口对象”的结构,所以容易混。

区别主要看目的:

维度代理模式装饰器模式
核心目的控制访问目标对象动态增强对象能力
关注点权限、远程调用、缓存、延迟加载、事务功能叠加、行为组合
客户端是否知道目标通常不关心真实对象在哪里通常主动组合多个装饰器
典型例子Spring AOP、RPC 代理、权限代理Java IO 流、饮料加料
关系代理更像中介或守门人装饰器更像功能插件

代理示例:

service = new UserServiceProxy(realService);

代理可能判断权限,不允许访问真实对象。

装饰器示例:

InputStream in = new BufferedInputStream(new FileInputStream(file));

装饰器是在原能力上叠加缓冲能力。

一句判断思路:如果重点是“要不要让你访问目标对象”,偏代理;如果重点是“在原对象基础上加新功能”,偏装饰器。

完整版教学

一、为什么二者容易混

代理模式和装饰器模式的类图很像:

接口
├── 真实对象
└── 包装对象(持有接口引用)

两者都可能写成:

class Wrapper implements Subject {
    private final Subject target;
}

所以不能只看结构,要看意图。

设计模式最重要的是意图,而不是长得像不像。

二、代理模式的意图是控制访问

代理对象站在客户端和真实对象之间,控制访问过程。

它可以:

  • 拒绝访问;
  • 延迟访问;
  • 远程转发;
  • 命中缓存后不访问;
  • 加事务后访问;
  • 记录日志后访问。

例如权限代理:

public void deleteUser(Long id) {
    if (!hasPermission()) {
        throw new AccessDeniedException();
    }
    target.deleteUser(id);
}

这里重点是访问控制:你能不能删、什么时候删、删之前要检查什么。

三、装饰器模式的意图是功能叠加

装饰器模式强调在不修改原对象的情况下动态叠加功能。

Java IO 是经典例子:

InputStream in = new BufferedInputStream(
        new FileInputStream("a.txt")
);

FileInputStream 提供文件读取能力,BufferedInputStream 增加缓冲能力。你还可以继续叠加其他装饰器。

装饰器的重点不是“控制你能不能访问 FileInputStream”,而是“给读取能力增加新的行为”。

四、从使用方式看差异

代理通常由框架或工厂创建,调用方不一定主动组装。

例如 Spring AOP:

@Autowired
private UserService userService;

你注入的可能已经是代理对象,但业务代码不需要手动套代理。

装饰器通常更强调显式组合:

Coffee coffee = new MilkDecorator(new SugarDecorator(new PlainCoffee()));

调用方可以按需要组合不同装饰器。

当然这不是绝对规则,但有助于判断意图。

五、二者能否同时出现

可以。同一个包装结构,既可能有代理意图,也可能有装饰器意图。

比如一个对象包装后既检查权限,又增加统计功能。具体归类要看主要目的。

面试时不用陷入“这个例子必须是哪一个”的争吵。更好的回答是:结构相似,区别看设计意图;控制访问偏代理,叠加职责偏装饰器。

六、从意图区分相似包装结构

同样包一层接口:权限代理可能拒绝 1 次未授权调用;压缩装饰器则仍执行原功能,只把输出从 100 KB 变成约 30 KB。

proxy: allow/deny -> target; decorator: target -> add capability -> result

这个推演把模式带来的收益和成本放在同一条调用链上。设计评审时既要确认结果正确,也要确认额外层次没有改变原有契约。

七、边界、代价与验证

检查维度应确认的内容
正确性缓存既可能是控制访问的代理,也可能被描述为能力增强;命名最终要服从上下文中的主要意图。
适用边界代理常由框架或服务端决定真实对象的访问方式,装饰器通常由客户端按能力组合;这不是绝对语法规则,而是意图线索。
测试证据覆盖正常路径、异常路径、空值或未知类型,并断言调用次数与顺序
运行成本记录额外对象、调用层数、延迟、线程或内存开销,不用模式名称代替测量

模式落地后要用单元测试验证独立职责,用集成测试验证对象装配和真实调用入口。涉及并发、远程或异步时,还要补充竞态、超时、重复执行与资源释放测试。

易错点:缓存既可能是控制访问的代理,也可能被描述为能力增强;命名最终要服从上下文中的主要意图。

八、常见误区与追问

  • 误区:只要类里持有同接口对象,它就是装饰器。 代理、装饰器甚至责任链都可能有相同结构,不能脱离职责与调用语义判断。
  • 误区:代理类存在,就代表所有调用都会经过代理。 只有客户端持有并调用代理引用时增强才生效;绕过代理直接调用目标对象,调用链自然不会出现增强。
  • 误区:示例代码能运行,就代表模式边界设计正确。 能运行只证明一条路径,仍要检查异常、并发、顺序、生命周期以及新增实现时是否修改稳定代码。
  • 追问:日志增强更像代理还是装饰器? 若它统一治理目标访问更偏代理;若作为可自由叠加的组件能力也可视作装饰器,需要说明语境。
  • 追问:代理模式和装饰器模式能只靠类图区分吗? 不能;两者都可能包装同一接口,必须结合“控制访问”还是“叠加职责”的设计意图区分。
  • 追问:怎样验证这套设计没有改变原有语义? 先做基线行为测试,再对新增层验证返回值、异常类型、调用次数和顺序;性能敏感处还要比较改造前后的量化指标。

九、加强记忆

代理像门卫,决定你能不能进、怎么进、进之前做什么;装饰器像外挂组件,在原能力上叠加新能力。结构可能相似,但意图不同,这是区分它们的关键。