← 返回题目列表

装饰器模式、适配器模式和责任链模式有什么区别?

高频 中等 第 6 / 25 题 更新于 2026/08/01
装饰器模式适配器模式责任链模式设计模式对比

简化版

装饰器模式是在不改变接口的前提下给对象叠加能力;适配器模式是把不兼容接口转换成客户端需要的接口;责任链模式是让请求沿多个处理器传递,直到被处理或全部经过。三者都可能“包一层”,但设计意图完全不同:增强、转换、传递。

详细版

面试里容易把这三个模式混在一起,因为代码形态上都可能出现“持有另一个对象”的结构。

装饰器关注运行时增强。它通常和被装饰对象实现同一个接口,调用前后增加能力,例如给输入流加缓冲、给数据源加加密、给服务加日志。

适配器关注接口兼容。它把旧接口、第三方接口、遗留系统接口转换成当前系统期望的接口,重点不是增强,而是让原本不能一起工作的对象能协作。

责任链关注请求流转。每个处理器决定是否处理、是否继续传递,常见于过滤器链、审批流、校验链、网关插件链。

可以这样区分:

  • 装饰器:接口通常不变,能力叠加;
  • 适配器:接口发生转换,解决不兼容;
  • 责任链:多个处理器按链传递请求,处理权可流转;
  • 装饰器强调“包着同一个对象继续增强”;
  • 责任链强调“请求经过多个节点”。

答题时不要只说“多包一层”,要说清楚这层解决的核心问题。

完整版教学

一、为什么这三个模式容易混淆

这三个模式都可能出现一个对象持有另一个对象的写法。比如装饰器持有被装饰对象,适配器持有被适配对象,责任链处理器持有下一个处理器。只看 UML 或字段结构,很容易觉得它们都差不多。

Decorator -> component
Adapter   -> adaptee
Handler   -> nextHandler

真正区分设计模式,不能只看“有没有组合”,要看组合这层的意图。装饰器是增强原对象能力,适配器是转换接口,责任链是安排请求处理路径。

记忆钩子:装饰器看增强,适配器看转换,责任链看传递。

二、装饰器模式的判断标准

装饰器模式通常要求装饰器和被装饰对象暴露同一套核心接口。客户端原来能调用 component.operation(),包上装饰器后仍然调用 operation(),只是执行过程中多了能力。

interface DataSource {
    String read();
    void write(String data);
}

class EncryptionDataSource implements DataSource {
    private final DataSource target;

    public String read() {
        return decrypt(target.read());
    }

    public void write(String data) {
        target.write(encrypt(data));
    }
}

这里客户端不需要知道底层是普通文件、压缩文件还是加密文件。接口没变,能力叠加了。装饰器的核心收益是运行时组合,比如加密、压缩、缓存、统计可以按不同顺序叠加。

三、适配器模式的判断标准

适配器模式的核心是接口不兼容。客户端期望 PaymentClient.pay(),第三方 SDK 提供的是 ThirdPartyCharge.charge()。适配器把一种接口转换成另一种接口。

interface PaymentClient {
    PayResult pay(PayCommand command);
}

class ThirdPartyPayAdapter implements PaymentClient {
    private final ThirdPartyCharge charge;

    public PayResult pay(PayCommand command) {
        ChargeRequest request = convert(command);
        ChargeResponse response = charge.charge(request);
        return convert(response);
    }
}

这层主要不是为了增强,而是为了让客户端不用感知第三方 SDK 的接口形态。如果移除适配器,客户端根本不能以统一接口调用第三方能力。适配器的关键词是“兼容旧接口、第三方接口、遗留系统”。

四、责任链模式的判断标准

责任链模式里,请求会沿着多个处理器流转。每个处理器可以处理一部分,也可以决定是否继续。比如网关请求要经过鉴权、限流、参数校验、路由、审计。

Request
  -> AuthHandler
  -> RateLimitHandler
  -> ValidateHandler
  -> RouteHandler
  -> Response

代码形态可能是:

interface Handler {
    void handle(Request request, Chain chain);
}

class AuthHandler implements Handler {
    public void handle(Request request, Chain chain) {
        checkAuth(request);
        chain.next(request);
    }
}

它和装饰器相似,因为也有“链”。区别在于责任链的每个节点通常面向同一个请求上下文,重点是流程编排和处理权传递;装饰器重点是让一个对象的能力被一层层增强。

五、用一个日志场景区分三者

假设系统要处理订单请求,日志可能出现在三种模式中。装饰器里,日志装饰器包住 OrderService,调用前后记录耗时。适配器里,日志只是转换第三方响应时的辅助动作,不是模式主角。责任链里,日志处理器作为请求链上的一个节点记录请求上下文。

装饰器:
  LoggingOrderService(OrderService target).createOrder()
  目标: 增强 OrderService

适配器:
  ThirdPartyOrderAdapter.submit()
  目标: 转换 ThirdParty API

责任链:
  Auth -> Log -> Validate -> Route
  目标: 安排请求处理流程

同样出现日志,不代表都是装饰器。判断时要看日志所在这层的主要职责。如果它的主职责是增强原接口,才更像装饰器。

六、用数字例子理解组合爆炸问题

装饰器常用于避免继承组合爆炸。比如一个数据源有 3 种增强:加密、压缩、缓存。用继承可能出现 2^3 = 8 种组合类:加密文件、压缩文件、缓存文件、加密压缩文件等。装饰器只需要 3 个增强类,运行时按需组合。

继承:
  Plain
  Encrypted
  Compressed
  Cached
  EncryptedCompressed
  EncryptedCached
  CompressedCached
  EncryptedCompressedCached

装饰器:
  EncryptionDecorator
  CompressionDecorator
  CacheDecorator
  按需包裹组合

责任链也能组合多个处理器,但目的不是避免同一对象的能力组合爆炸,而是把请求处理流程拆成多个节点。适配器则通常不讨论组合爆炸,它解决的是接口对接成本。

七、面试中怎么快速判断

面试官给一段代码时,可以按三个问题判断。第一,这层是否保持原接口并增强行为?如果是,偏装饰器。第二,这层是否把一个不兼容接口转换成另一个接口?如果是,偏适配器。第三,请求是否沿多个处理器流动,并且每个处理器决定是否继续?如果是,偏责任链。

判断问题更像哪种模式
接口不变,能力叠加装饰器
接口转换,兼容第三方/旧系统适配器
多节点处理请求,决定是否继续责任链
重点是对象能力组合装饰器
重点是流程处理顺序责任链
重点是让不能协作的接口协作适配器

这张表比背定义更有用,因为面试题经常给具体场景,而不是直接问概念。

八、常见误区与追问

  • 误区:只要包了一层就是装饰器。 适配器、代理、责任链也可能包一层,关键要看意图。
  • 误区:装饰器和责任链都能形成链,所以是同一种模式。 装饰器链增强对象能力,责任链传递请求处理权。
  • 误区:适配器也能加日志,所以适配器就是装饰器。 如果主要目的是接口转换,它仍然是适配器。
  • 追问:Java IO 更像装饰器还是责任链? 更像装饰器,因为 InputStream 接口保持一致,能力逐层叠加。
  • 追问:Servlet Filter 更像什么? 更像责任链,因为请求沿过滤器链流转,每个过滤器决定是否继续。
  • 追问:装饰器和适配器能组合使用吗? 可以,先用适配器统一接口,再用装饰器叠加缓存、日志、限流等能力。
  • 追问:判断模式时看结构还是意图? 结构只能辅助,最终看设计意图和变化方向。

九、加强记忆

这三种模式用三个动词记:装饰器是“增强”,适配器是“转换”,责任链是“传递”。看到持有另一个对象不要急着下结论,先问这层到底在增强同一接口、转换不兼容接口,还是安排请求沿链处理。