知识模块
☕ Java 知识模块
十一、设计模式
适配器与装饰器模式

适配器与装饰器模式

适配器模式和装饰器模式都是结构型设计模式,但目的不同。

一、适配器模式

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.0

3. 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 中的应用?

  • HttpServletRequestWrapper
  • HttpServletResponseWrapper
  • BeanWrapper

六、最佳实践

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日