适配器与装饰器模式
适配器模式和装饰器模式都是结构型设计模式,但目的不同。
一、适配器模式
1. 核心概念
适配器模式将一个类的接口转换成客户期望的另一个接口,使接口不兼容的类可以一起工作。
目标接口 适配器 被适配者
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Target │◄───│ Adapter │───►│ Adaptee │
│+request()│ │-adaptee │ │+specific │
└──────────┘ │+request()│ │ Request()│
└──────────┘ └──────────┘2. 类适配器
// 目标接口
public interface Target {
void request();
}
// 被适配者
public class Adaptee {
public void specificRequest() {
System.out.println("被适配者的特殊请求");
}
}
// 类适配器(继承)
public class ClassAdapter extends Adaptee implements Target {
@Override
public void request() {
specificRequest(); // 调用父类方法
}
}
// 使用
Target target = new ClassAdapter();
target.request();3. 对象适配器
// 对象适配器(组合)
public class ObjectAdapter implements Target {
private Adaptee adaptee;
public ObjectAdapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void request() {
adaptee.specificRequest(); // 委托给被适配者
}
}
// 使用
Target target = new ObjectAdapter(new Adaptee());
target.request();4. 实际应用
Java I/O:
// InputStreamReader 是适配器
// 将 InputStream(字节流)适配为 Reader(字符流)
InputStream is = new FileInputStream("test.txt");
Reader reader = new InputStreamReader(is, "UTF-8");Arrays.asList():
// 将数组适配为 List
String[] array = {"a", "b", "c"};
List<String> list = Arrays.asList(array);Spring MVC HandlerAdapter:
// 不同类型的 Handler 使用不同的适配器
public interface HandlerAdapter {
boolean supports(Object handler);
ModelAndView handle(HttpServletRequest request,
HttpServletResponse response,
Object handler);
}
// Controller 适配器
public class SimpleControllerHandlerAdapter implements HandlerAdapter {
@Override
public boolean supports(Object handler) {
return handler instanceof Controller;
}
@Override
public ModelAndView handle(HttpServletRequest request,
HttpServletResponse response,
Object handler) {
return ((Controller) handler).handleRequest(request, response);
}
}二、装饰器模式
1. 核心概念
装饰器模式动态地给对象添加额外职责,比继承更灵活。
┌─────────────────────────────────────┐
│ Component (抽象组件) │
│ + operation(): void │
└──────────────┬──────────────────────┘
│
┌─────────┴─────────┐
│ │
┌────┴────┐ ┌──────┴──────┐
│Concrete │ │ Decorator │
│Component│ │ -component │
└─────────┘ │ +operation()│
└──────┬──────┘
│
┌──────────┼──────────┐
▼ ▼ ▼
┌────────┐ ┌────────┐ ┌────────┐
│Decorator│ │Decorator│ │Decorator│
│ A │ │ B │ │ C │
└────────┘ └────────┘ └────────┘2. 实现
// 抽象组件
public interface Coffee {
String getDescription();
double getCost();
}
// 具体组件
public class SimpleCoffee implements Coffee {
@Override
public String getDescription() {
return "简单咖啡";
}
@Override
public double getCost() {
return 10.0;
}
}
// 抽象装饰器
public abstract class CoffeeDecorator implements Coffee {
protected Coffee coffee;
public CoffeeDecorator(Coffee coffee) {
this.coffee = coffee;
}
}
// 具体装饰器:牛奶
public class MilkDecorator extends CoffeeDecorator {
public MilkDecorator(Coffee coffee) {
super(coffee);
}
@Override
public String getDescription() {
return coffee.getDescription() + " + 牛奶";
}
@Override
public double getCost() {
return coffee.getCost() + 2.0;
}
}
// 具体装饰器:糖
public class SugarDecorator extends CoffeeDecorator {
public SugarDecorator(Coffee coffee) {
super(coffee);
}
@Override
public String getDescription() {
return coffee.getDescription() + " + 糖";
}
@Override
public double getCost() {
return coffee.getCost() + 1.0;
}
}
// 使用
Coffee coffee = new SimpleCoffee();
coffee = new MilkDecorator(coffee);
coffee = new SugarDecorator(coffee);
System.out.println(coffee.getDescription()); // 简单咖啡 + 牛奶 + 糖
System.out.println(coffee.getCost()); // 13.03. Java I/O 装饰器
// InputStream 是抽象组件
// FileInputStream 是具体组件
// BufferedInputStream 是装饰器
// DataInputStream 是装饰器
InputStream is = new FileInputStream("test.txt");
is = new BufferedInputStream(is); // 添加缓冲功能
is = new DataInputStream(is); // 添加数据读取功能4. 实际应用
Spring Wrapper:
// HttpServletRequestWrapper
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
public XssHttpServletRequestWrapper(HttpServletRequest request) {
super(request);
}
@Override
public String getParameter(String name) {
String value = super.getParameter(name);
return cleanXSS(value); // 添加 XSS 过滤功能
}
private String cleanXSS(String value) {
// XSS 过滤逻辑
return value;
}
}三、两种模式对比
| 对比项 | 适配器模式 | 装饰器模式 |
|---|---|---|
| 目的 | 接口转换 | 功能增强 |
| 改变 | 接口形式 | 对象职责 |
| 透明性 | 对客户端透明 | 对客户端透明 |
| 关系 | 组合或继承 | 组合 |
| 应用场景 | 接口不兼容 | 动态添加功能 |
适配器:A ──→ Adapter ──→ B(让 A 适配 B 的接口)
装饰器:A ──→ Decorator ──→ A+(增强 A 的功能)四、代理 vs 装饰器 vs 适配器
| 模式 | 目的 | 关系 |
|---|---|---|
| 代理 | 控制访问 | 代理和目标对象生命周期相同 |
| 装饰器 | 增强功能 | 可动态添加多个装饰器 |
| 适配器 | 接口转换 | 适配器和被适配者接口不同 |
// 代理:控制访问
public class Proxy implements Subject {
private RealSubject realSubject;
public void request() {
if (checkPermission()) {
realSubject.request();
}
}
}
// 装饰器:增强功能
public class Decorator implements Subject {
private Subject subject;
public void request() {
log("before");
subject.request();
log("after");
}
}
// 适配器:接口转换
public class Adapter implements Target {
private Adaptee adaptee;
public void request() {
adaptee.specificRequest(); // 接口转换
}
}五、面试高频问题
Q1: 适配器模式优缺点?
优点:
- 让不兼容的类一起工作
- 提高类复用性
- 灵活性好
缺点:
- 增加系统复杂度
- 过多使用会让系统凌乱
Q2: 装饰器模式优缺点?
优点:
- 比继承更灵活
- 动态组合功能
- 符合开闭原则
缺点:
- 会产生很多小对象
- 装饰顺序敏感
- 排错困难
Q3: 为什么推荐对象适配器?
- 组合优于继承
- 更灵活,可适配多个被适配者
- 符合合成复用原则
Q4: Java I/O 为什么用装饰器?
- 避免类爆炸(各种组合)
- 灵活组合功能
- 符合单一职责原则
Q5: 装饰器模式在 Spring 中的应用?
HttpServletRequestWrapperHttpServletResponseWrapperBeanWrapper
六、最佳实践
1. 适配器选择组合
// 推荐:对象适配器
public class Adapter implements Target {
private Adaptee adaptee;
// ...
}2. 装饰器保持接口一致
// 装饰器应该和被装饰对象实现相同接口
public class Decorator implements Component {
// 不改变接口,只增强功能
}3. 装饰器可嵌套
// 多个装饰器可以叠加使用
Coffee coffee = new SimpleCoffee();
coffee = new MilkDecorator(coffee);
coffee = new SugarDecorator(coffee);
coffee = new WhipDecorator(coffee);更新时间:2026年3月16日