← 返回题目列表

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

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

简化版

装饰器模式关注“动态叠加功能”,代理模式关注“控制访问”。两者结构上都可能包装同接口对象,但装饰器像给对象加插件,代理更像中间人或门卫。

详细版

装饰器和代理都可能长这样:

class Wrapper implements Service {
    private final Service target;
}

所以它们不能只看代码结构,要看设计意图。

维度装饰器模式代理模式
核心意图增强功能、叠加能力控制访问、隐藏访问细节
典型场景Java IO、加密压缩、UI 增强Spring AOP、RPC、权限、缓存
组合方式常常多层显式组合常由框架或工厂创建
关注点原能力基础上加新能力是否访问、何时访问、如何访问
类比插件、外套、加料门卫、中介、替身

装饰器例子:

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

重点是给文件输入流增加缓冲能力。

代理例子:

UserService service = proxyFactory.create(UserService.class);

重点可能是权限校验、事务控制、远程调用转发。

面试中更稳的回答是:结构相似,意图不同;功能叠加偏装饰器,访问控制偏代理。

完整版教学

一、为什么二者最容易混

装饰器和代理都属于结构型模式,而且都可能使用共同接口。

例如:

interface Component {
    void operation();
}

装饰器:

class LogDecorator implements Component {
    private final Component component;
}

代理:

class ComponentProxy implements Component {
    private final Component target;
}

从类结构看,几乎一样。因此区分它们要看“包装的目的”。

二、装饰器的目的:能力叠加

装饰器模式通常是为了让对象获得更多能力。

比如:

DataSource source = new CompressionDecorator(
        new EncryptionDecorator(
                new FileDataSource()
        )
);

这里重点是:

  • 文件数据源提供基础读写;
  • 加密装饰器增加加解密;
  • 压缩装饰器增加压缩解压;
  • 多个能力可以自由组合。

如果去掉装饰器,基础对象仍然可以工作,只是能力少一些。

三、代理的目的:访问控制

代理更强调控制访问过程。

例如权限代理:

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

这里重点不是给 deleteUser 增加一种业务能力,而是决定是否允许访问目标对象。

远程代理也是类似:

userService.getUser(id);

表面是本地调用,代理对象实际把请求发到远程服务。它隐藏的是访问细节。

四、缓存到底算代理还是装饰器

缓存很容易让人纠结。

如果缓存的主要目的是控制是否访问真实对象,命中缓存就不调目标对象,它更像代理。

if (cache.containsKey(key)) {
    return cache.get(key);
}
return target.query(id);

如果缓存被设计成一个可组合增强层,也可以看出装饰器味道。

这说明设计模式不是死标签。真实项目里要看主意图。如果面试官追问,回答“结构相似,按主要意图区分”比硬分类更稳。

五、Spring AOP 更偏代理还是装饰器

Spring AOP 更常被归为代理模式。

原因是 Spring 通过代理对象控制方法调用,在调用前后织入事务、权限、日志等切面逻辑。调用方通常不是主动组合多层装饰器,而是容器返回一个代理对象。

虽然从“增强行为”的角度看它和装饰器也有相似处,但主意图是通过代理拦截方法调用。

六、能力叠加和访问控制的分界

压缩装饰器把 100 KB 响应变成 30 KB,核心是增加能力;权限代理拒绝 1 个未授权请求,核心是决定能否访问目标。

decorator -> delegate + enhance; proxy -> guard/route -> target

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

七、边界、代价与验证

检查维度应确认的内容
正确性结构相似是类图层面的事实,意图不同才是面试回答的核心。
适用边界两者可同时存在,例如权限代理外再套压缩装饰器;模式可以组合,名称取决于该对象承担的主要职责。
测试证据覆盖正常路径、异常路径、空值或未知类型,并断言调用次数与顺序
运行成本记录额外对象、调用层数、延迟、线程或内存开销,不用模式名称代替测量

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

易错点:结构相似是类图层面的事实,意图不同才是面试回答的核心。

八、常见误区与追问

  • 误区:缓存永远只能归为代理模式。 若强调避免访问真实对象更像代理;若强调为组件叠加可组合缓存能力,也可能按装饰器讨论。
  • 误区:装饰器可以任意改变组件接口。 透明装饰依赖共同的 Component 契约;随意增加只能向下转型调用的方法,会破坏可替换性和继续组合的能力。
  • 误区:示例代码能运行,就代表模式边界设计正确。 能运行只证明一条路径,仍要检查异常、并发、顺序、生命周期以及新增实现时是否修改稳定代码。
  • 追问:AOP 增强为什么通常称为代理? 框架向客户端提供目标替身并控制方法访问,访问治理意图更突出。
  • 追问:装饰器链越长,扩展性就一定越好吗? 不一定;链越长,顺序、异常传播和排障成本越高,应由装配层集中管理并限制职责粒度。
  • 追问:怎样验证这套设计没有改变原有语义? 先做基线行为测试,再对新增层验证返回值、异常类型、调用次数和顺序;性能敏感处还要比较改造前后的量化指标。

九、加强记忆

装饰器和代理的代码外形可能很像,但装饰器问的是“还能加什么能力”,代理问的是“访问目标时要经过什么控制”。一个像加料,一个像门卫,按意图区分最稳。