深入理解设计模式之适配器模式

深入理解设计模式之适配器模式(Adapter Pattern)

一、引言

在软件开发中,我们经常遇到这样的问题:已有的类接口与需要的接口不匹配。比如你有一个欧标插头的电器,但酒店只提供美标插座;或者你需要整合一个第三方SDK,但它的接口与你的系统不兼容。这时,适配器模式就派上用场了。

适配器模式就像现实中的电源转换器,它充当两个不兼容接口之间的桥梁,使得原本无法协同工作的类可以一起工作。本文将深入探讨适配器模式的原理、实现方式,并结合Spring、JDBC等实际框架应用,帮助你全面掌握这一重要的设计模式。

二、什么是适配器模式

2.1 定义

适配器模式(Adapter Pattern)是一种结构型设计模式,它将一个类的接口转换成客户端期望的另一个接口。适配器让原本接口不兼容的类可以相互合作。

2.2 核心思想

  • 接口转换:将一个接口转换为另一个接口
  • 复用现有类:不修改现有代码的前提下实现兼容
  • 解耦:客户端与被适配者解耦
  • 两个方向:可以将新接口适配到旧系统,也可以将旧接口适配到新系统

2.3 模式结构

scss 复制代码
客户端使用的接口:
┌──────────────────────┐
│    <<interface>>     │
│       Target         │    目标接口(客户端期望的)
├──────────────────────┤
│ + request()          │
└──────────────────────┘
           △
           │ 实现
           │
     ┌─────┴──────────────────┐
     │                        │
┌────┴────────┐      ┌────────┴─────────┐
│  Adapter    │      │   Adaptee        │  被适配者(已存在的类)
├─────────────┤      ├──────────────────┤
│- adaptee    │◆────→│+ specificRequest │  特殊的方法
│+ request()  │      └──────────────────┘
└─────────────┘
     │
     └→ 调用adaptee.specificRequest()

2.4 适配器模式的三种形式

1. 类适配器(使用继承):

markdown 复制代码
┌───────────┐      ┌──────────┐
│  Target   │      │ Adaptee  │
└─────△─────┘      └────△─────┘
      │                 │
      │                 │
      └────┬────────────┘
           │ 多重继承
      ┌────┴─────┐
      │ Adapter  │
      └──────────┘

注:Java不支持多重继承,所以通常使用接口+类继承

2. 对象适配器(使用组合,推荐):

markdown 复制代码
┌───────────┐      ┌──────────┐
│  Target   │      │ Adaptee  │
└─────△─────┘      └──────────┘
      │                 △
      │                 │组合
      │            ┌────┴─────┐
      │            │          │
      └────────────┤ Adapter  │
                   └──────────┘

3. 接口适配器(缺省适配器):

scss 复制代码
┌────────────────────┐
│ <<interface>>      │
│  MultiMethod       │  多方法接口
├────────────────────┤
│+ method1()         │
│+ method2()         │
│+ method3()         │
│+ method4()         │
└─────────△──────────┘
          │
    ┌─────┴─────┐
    │  Abstract │  抽象适配器(提供空实现)
    │  Adapter  │
    └─────△─────┘
          │
          │
    ┌─────┴──────┐
    │  Concrete  │  具体类只需实现需要的方法
    │  Adapter   │
    └────────────┘

三、基础示例

3.1 场景:电源适配器

最经典的例子:中国电器(220V)需要在日本(110V)使用。

目标接口(客户端期望):

java 复制代码
/**
 * 目标接口:日本电源插座(110V)
 */
public interface JapanesePlug {
    /**
     * 提供110V电压
     */
    void provide110V();
}

被适配者(已存在的类):

java 复制代码
/**
 * 被适配者:中国电器(需要220V)
 */
public class ChineseAppliance {

    private String name;

    public ChineseAppliance(String name) {
        this.name = name;
    }

    /**
     * 使用220V电压工作
     */
    public void use220V() {
        System.out.println(name + " 正在使用220V电压工作");
    }

    public String getName() {
        return name;
    }
}

对象适配器:

java 复制代码
/**
 * 对象适配器:电源适配器(推荐方式)
 * 将220V的中国电器适配到110V的日本插座
 */
public class PowerAdapter implements JapanesePlug {

    private ChineseAppliance chineseAppliance;

    public PowerAdapter(ChineseAppliance chineseAppliance) {
        this.chineseAppliance = chineseAppliance;
    }

    @Override
    public void provide110V() {
        System.out.println("电源适配器工作中:将110V转换为220V");
        // 调用被适配者的方法
        chineseAppliance.use220V();
    }
}

类适配器(Java中使用接口+继承):

java 复制代码
/**
 * 类适配器:通过继承实现适配
 */
public class PowerAdapterByInheritance extends ChineseAppliance implements JapanesePlug {

    public PowerAdapterByInheritance(String name) {
        super(name);
    }

    @Override
    public void provide110V() {
        System.out.println("电源适配器工作中(类适配器):将110V转换为220V");
        // 直接调用父类方法
        use220V();
    }
}

客户端使用:

java 复制代码
public class PowerAdapterDemo {
    public static void main(String[] args) {
        System.out.println("=== 场景1:使用对象适配器 ===");

        // 中国电器
        ChineseAppliance laptop = new ChineseAppliance("笔记本电脑");

        // 使用适配器,让中国电器在日本使用
        JapanesePlug adapter = new PowerAdapter(laptop);
        adapter.provide110V();

        System.out.println("\n=== 场景2:使用类适配器 ===");

        JapanesePlug adapter2 = new PowerAdapterByInheritance("电吹风");
        adapter2.provide110V();
    }
}

输出:

diff 复制代码
=== 场景1:使用对象适配器 ===
电源适配器工作中:将110V转换为220V
笔记本电脑 正在使用220V电压工作

=== 场景2:使用类适配器 ===
电源适配器工作中(类适配器):将110V转换为220V
电吹风 正在使用220V电压工作

3.2 场景:读卡器(多种适配器)

读卡器可以读取不同类型的存储卡。

java 复制代码
/**
 * 目标接口:通用存储设备
 */
public interface StorageDevice {
    String readData();
    void writeData(String data);
}

/**
 * 被适配者1:SD卡
 */
public class SDCard {
    public String readSD() {
        return "SD卡数据";
    }

    public void writeSD(String data) {
        System.out.println("写入SD卡: " + data);
    }
}

/**
 * 被适配者2:TF卡(Micro SD)
 */
public class TFCard {
    public String readTF() {
        return "TF卡数据";
    }

    public void writeTF(String data) {
        System.out.println("写入TF卡: " + data);
    }
}

/**
 * 被适配者3:Memory Stick
 */
public class MemoryStick {
    public String readMS() {
        return "Memory Stick数据";
    }

    public void writeMS(String data) {
        System.out.println("写入Memory Stick: " + data);
    }
}

/**
 * SD卡适配器
 */
public class SDCardAdapter implements StorageDevice {
    private SDCard sdCard;

    public SDCardAdapter(SDCard sdCard) {
        this.sdCard = sdCard;
    }

    @Override
    public String readData() {
        return sdCard.readSD();
    }

    @Override
    public void writeData(String data) {
        sdCard.writeSD(data);
    }
}

/**
 * TF卡适配器
 */
public class TFCardAdapter implements StorageDevice {
    private TFCard tfCard;

    public TFCardAdapter(TFCard tfCard) {
        this.tfCard = tfCard;
    }

    @Override
    public String readData() {
        return tfCard.readTF();
    }

    @Override
    public void writeData(String data) {
        tfCard.writeTF(data);
    }
}

/**
 * Memory Stick适配器
 */
public class MemoryStickAdapter implements StorageDevice {
    private MemoryStick memoryStick;

    public MemoryStickAdapter(MemoryStick memoryStick) {
        this.memoryStick = memoryStick;
    }

    @Override
    public String readData() {
        return memoryStick.readMS();
    }

    @Override
    public void writeData(String data) {
        memoryStick.writeMS(data);
    }
}

/**
 * 客户端:电脑(只认识通用存储设备接口)
 */
public class Computer {

    public void readStorage(StorageDevice storage) {
        String data = storage.readData();
        System.out.println("电脑读取到: " + data);
    }

    public void writeStorage(StorageDevice storage, String data) {
        storage.writeData(data);
        System.out.println("电脑写入完成");
    }
}

/**
 * 测试类
 */
public class CardReaderDemo {
    public static void main(String[] args) {
        Computer computer = new Computer();

        // 使用SD卡
        System.out.println("=== 使用SD卡 ===");
        SDCard sdCard = new SDCard();
        StorageDevice sdAdapter = new SDCardAdapter(sdCard);
        computer.readStorage(sdAdapter);
        computer.writeStorage(sdAdapter, "文档.doc");

        // 使用TF卡
        System.out.println("\n=== 使用TF卡 ===");
        TFCard tfCard = new TFCard();
        StorageDevice tfAdapter = new TFCardAdapter(tfCard);
        computer.readStorage(tfAdapter);

        // 使用Memory Stick
        System.out.println("\n=== 使用Memory Stick ===");
        MemoryStick ms = new MemoryStick();
        StorageDevice msAdapter = new MemoryStickAdapter(ms);
        computer.readStorage(msAdapter);
    }
}

四、实际生产场景应用

4.1 场景:第三方支付适配器

系统需要集成多个支付渠道(支付宝、微信、银联),但它们的API各不相同。

java 复制代码
/**
 * 目标接口:统一支付接口
 */
public interface PaymentProcessor {
    /**
     * 支付
     * @param orderId 订单号
     * @param amount 金额
     * @return 支付结果
     */
    PaymentResult pay(String orderId, double amount);

    /**
     * 退款
     * @param orderId 订单号
     * @param amount 金额
     * @return 退款结果
     */
    PaymentResult refund(String orderId, double amount);

    /**
     * 查询订单
     * @param orderId 订单号
     * @return 订单状态
     */
    String queryOrder(String orderId);
}

/**
 * 支付结果
 */
class PaymentResult {
    private boolean success;
    private String message;
    private String transactionId;

    public PaymentResult(boolean success, String message, String transactionId) {
        this.success = success;
        this.message = message;
        this.transactionId = transactionId;
    }

    // Getters
    public boolean isSuccess() { return success; }
    public String getMessage() { return message; }
    public String getTransactionId() { return transactionId; }

    @Override
    public String toString() {
        return "PaymentResult{" +
                "success=" + success +
                ", message='" + message + '\'' +
                ", transactionId='" + transactionId + '\'' +
                '}';
    }
}

被适配者:各第三方支付SDK

java 复制代码
/**
 * 被适配者1:支付宝SDK(假设的API)
 */
public class AlipaySDK {

    public AlipayResponse trade(String outTradeNo, String amount) {
        System.out.println("[支付宝] 发起支付: 订单=" + outTradeNo + ", 金额=" + amount);
        // 模拟支付成功
        AlipayResponse response = new AlipayResponse();
        response.code = "10000";
        response.msg = "Success";
        response.tradeNo = "2025" + System.currentTimeMillis();
        return response;
    }

    public AlipayResponse refundTrade(String outTradeNo, String refundAmount) {
        System.out.println("[支付宝] 发起退款: 订单=" + outTradeNo + ", 金额=" + refundAmount);
        AlipayResponse response = new AlipayResponse();
        response.code = "10000";
        response.msg = "Refund Success";
        response.tradeNo = "R" + System.currentTimeMillis();
        return response;
    }

    public AlipayResponse query(String outTradeNo) {
        System.out.println("[支付宝] 查询订单: " + outTradeNo);
        AlipayResponse response = new AlipayResponse();
        response.code = "10000";
        response.tradeStatus = "TRADE_SUCCESS";
        return response;
    }

    // 支付宝响应对象
    public static class AlipayResponse {
        public String code;
        public String msg;
        public String tradeNo;
        public String tradeStatus;
    }
}

/**
 * 被适配者2:微信支付SDK(假设的API)
 */
public class WeChatPaySDK {

    public WeChatPayResult unifiedOrder(String orderNo, int totalFee) {
        System.out.println("[微信支付] 统一下单: 订单=" + orderNo + ", 金额=" + totalFee + "分");
        WeChatPayResult result = new WeChatPayResult();
        result.returnCode = "SUCCESS";
        result.resultCode = "SUCCESS";
        result.transactionId = "WX" + System.currentTimeMillis();
        return result;
    }

    public WeChatPayResult refundOrder(String orderNo, int refundFee) {
        System.out.println("[微信支付] 申请退款: 订单=" + orderNo + ", 金额=" + refundFee + "分");
        WeChatPayResult result = new WeChatPayResult();
        result.returnCode = "SUCCESS";
        result.resultCode = "SUCCESS";
        result.refundId = "R" + System.currentTimeMillis();
        return result;
    }

    public WeChatPayResult orderQuery(String orderNo) {
        System.out.println("[微信支付] 查询订单: " + orderNo);
        WeChatPayResult result = new WeChatPayResult();
        result.returnCode = "SUCCESS";
        result.tradeState = "SUCCESS";
        return result;
    }

    // 微信支付响应对象
    public static class WeChatPayResult {
        public String returnCode;
        public String resultCode;
        public String transactionId;
        public String refundId;
        public String tradeState;
    }
}

/**
 * 被适配者3:银联支付SDK(假设的API)
 */
public class UnionPaySDK {

    public boolean consume(String orderId, String amt) {
        System.out.println("[银联支付] 消费交易: 订单=" + orderId + ", 金额=" + amt + "元");
        return true;
    }

    public boolean refund(String orderId, String amt) {
        System.out.println("[银联支付] 退货交易: 订单=" + orderId + ", 金额=" + amt + "元");
        return true;
    }

    public String queryTrans(String orderId) {
        System.out.println("[银联支付] 查询交易: " + orderId);
        return "00"; // 00表示成功
    }
}

适配器实现:

java 复制代码
/**
 * 支付宝适配器
 */
public class AlipayAdapter implements PaymentProcessor {

    private AlipaySDK alipaySDK;

    public AlipayAdapter() {
        this.alipaySDK = new AlipaySDK();
    }

    @Override
    public PaymentResult pay(String orderId, double amount) {
        // 将amount转换为字符串格式
        String amountStr = String.format("%.2f", amount);

        // 调用支付宝SDK
        AlipaySDK.AlipayResponse response = alipaySDK.trade(orderId, amountStr);

        // 转换响应格式
        boolean success = "10000".equals(response.code);
        return new PaymentResult(success, response.msg, response.tradeNo);
    }

    @Override
    public PaymentResult refund(String orderId, double amount) {
        String amountStr = String.format("%.2f", amount);
        AlipaySDK.AlipayResponse response = alipaySDK.refundTrade(orderId, amountStr);

        boolean success = "10000".equals(response.code);
        return new PaymentResult(success, response.msg, response.tradeNo);
    }

    @Override
    public String queryOrder(String orderId) {
        AlipaySDK.AlipayResponse response = alipaySDK.query(orderId);
        return response.tradeStatus;
    }
}

/**
 * 微信支付适配器
 */
public class WeChatPayAdapter implements PaymentProcessor {

    private WeChatPaySDK weChatPaySDK;

    public WeChatPayAdapter() {
        this.weChatPaySDK = new WeChatPaySDK();
    }

    @Override
    public PaymentResult pay(String orderId, double amount) {
        // 微信支付使用分为单位
        int totalFee = (int) (amount * 100);

        // 调用微信支付SDK
        WeChatPaySDK.WeChatPayResult result = weChatPaySDK.unifiedOrder(orderId, totalFee);

        // 转换响应格式
        boolean success = "SUCCESS".equals(result.returnCode) && "SUCCESS".equals(result.resultCode);
        return new PaymentResult(success, "微信支付成功", result.transactionId);
    }

    @Override
    public PaymentResult refund(String orderId, double amount) {
        int refundFee = (int) (amount * 100);
        WeChatPaySDK.WeChatPayResult result = weChatPaySDK.refundOrder(orderId, refundFee);

        boolean success = "SUCCESS".equals(result.returnCode);
        return new PaymentResult(success, "退款成功", result.refundId);
    }

    @Override
    public String queryOrder(String orderId) {
        WeChatPaySDK.WeChatPayResult result = weChatPaySDK.orderQuery(orderId);
        return result.tradeState;
    }
}

/**
 * 银联支付适配器
 */
public class UnionPayAdapter implements PaymentProcessor {

    private UnionPaySDK unionPaySDK;

    public UnionPayAdapter() {
        this.unionPaySDK = new UnionPaySDK();
    }

    @Override
    public PaymentResult pay(String orderId, double amount) {
        String amountStr = String.valueOf(amount);

        boolean success = unionPaySDK.consume(orderId, amountStr);

        return new PaymentResult(success, success ? "支付成功" : "支付失败", orderId);
    }

    @Override
    public PaymentResult refund(String orderId, double amount) {
        String amountStr = String.valueOf(amount);
        boolean success = unionPaySDK.refund(orderId, amountStr);

        return new PaymentResult(success, success ? "退款成功" : "退款失败", orderId);
    }

    @Override
    public String queryOrder(String orderId) {
        String code = unionPaySDK.queryTrans(orderId);
        return "00".equals(code) ? "SUCCESS" : "FAIL";
    }
}

支付工厂和客户端:

java 复制代码
/**
 * 支付处理器工厂
 */
public class PaymentProcessorFactory {

    public static PaymentProcessor getProcessor(String paymentType) {
        switch (paymentType.toLowerCase()) {
            case "alipay":
                return new AlipayAdapter();
            case "wechat":
                return new WeChatPayAdapter();
            case "unionpay":
                return new UnionPayAdapter();
            default:
                throw new IllegalArgumentException("不支持的支付类型: " + paymentType);
        }
    }
}

/**
 * 订单服务(客户端)
 */
public class OrderService {

    public void processPayment(String orderId, double amount, String paymentType) {
        System.out.println("\n========== 处理订单支付 ==========");
        System.out.println("订单号: " + orderId);
        System.out.println("金额: ¥" + amount);
        System.out.println("支付方式: " + paymentType);
        System.out.println("================================\n");

        // 获取对应的支付处理器(适配器)
        PaymentProcessor processor = PaymentProcessorFactory.getProcessor(paymentType);

        // 统一调用支付接口
        PaymentResult result = processor.pay(orderId, amount);

        System.out.println("\n支付结果: " + result);

        // 查询订单状态
        String status = processor.queryOrder(orderId);
        System.out.println("订单状态: " + status + "\n");
    }
}

/**
 * 测试类
 */
public class PaymentAdapterDemo {
    public static void main(String[] args) {
        OrderService orderService = new OrderService();

        // 使用支付宝支付
        orderService.processPayment("ORDER001", 99.99, "alipay");

        // 使用微信支付
        orderService.processPayment("ORDER002", 199.50, "wechat");

        // 使用银联支付
        orderService.processPayment("ORDER003", 299.00, "unionpay");
    }
}

4.2 场景:日志框架适配器

系统需要支持多种日志框架(Log4j、Logback、JDK Logging),但又希望统一使用。

java 复制代码
/**
 * 目标接口:统一日志接口
 */
public interface Logger {
    void debug(String message);
    void info(String message);
    void warn(String message);
    void error(String message);
}

/**
 * 被适配者1:Log4j(假设的API)
 */
class Log4jLogger {
    private String name;

    public Log4jLogger(String name) {
        this.name = name;
    }

    public void log(int level, String msg) {
        String levelName = getLevelName(level);
        System.out.println("[Log4j] " + levelName + " - " + name + ": " + msg);
    }

    private String getLevelName(int level) {
        switch (level) {
            case 0: return "DEBUG";
            case 1: return "INFO";
            case 2: return "WARN";
            case 3: return "ERROR";
            default: return "UNKNOWN";
        }
    }
}

/**
 * 被适配者2:JDK Logging
 */
class JdkLogger {
    private String name;

    public JdkLogger(String name) {
        this.name = name;
    }

    public void logp(String level, String sourceClass, String sourceMethod, String msg) {
        System.out.println("[JDK] " + level + " - " + name + "." + sourceMethod + ": " + msg);
    }
}

/**
 * Log4j适配器
 */
public class Log4jAdapter implements Logger {
    private Log4jLogger log4jLogger;

    public Log4jAdapter(String name) {
        this.log4jLogger = new Log4jLogger(name);
    }

    @Override
    public void debug(String message) {
        log4jLogger.log(0, message);
    }

    @Override
    public void info(String message) {
        log4jLogger.log(1, message);
    }

    @Override
    public void warn(String message) {
        log4jLogger.log(2, message);
    }

    @Override
    public void error(String message) {
        log4jLogger.log(3, message);
    }
}

/**
 * JDK Logging适配器
 */
public class JdkLoggerAdapter implements Logger {
    private JdkLogger jdkLogger;
    private String className;

    public JdkLoggerAdapter(String name) {
        this.jdkLogger = new JdkLogger(name);
        this.className = name;
    }

    @Override
    public void debug(String message) {
        jdkLogger.logp("FINE", className, "debug", message);
    }

    @Override
    public void info(String message) {
        jdkLogger.logp("INFO", className, "info", message);
    }

    @Override
    public void warn(String message) {
        jdkLogger.logp("WARNING", className, "warn", message);
    }

    @Override
    public void error(String message) {
        jdkLogger.logp("SEVERE", className, "error", message);
    }
}

/**
 * 日志工厂
 */
public class LoggerFactory {
    private static String loggerType = "log4j"; // 可配置

    public static Logger getLogger(String name) {
        if ("log4j".equals(loggerType)) {
            return new Log4jAdapter(name);
        } else if ("jdk".equals(loggerType)) {
            return new JdkLoggerAdapter(name);
        }
        throw new IllegalArgumentException("不支持的日志类型");
    }

    public static void setLoggerType(String type) {
        loggerType = type;
    }
}

/**
 * 测试
 */
class LoggerAdapterDemo {
    public static void main(String[] args) {
        // 使用Log4j
        System.out.println("=== 使用Log4j ===");
        LoggerFactory.setLoggerType("log4j");
        Logger logger1 = LoggerFactory.getLogger("com.example.Service");
        logger1.info("应用启动");
        logger1.warn("内存使用率较高");

        // 切换到JDK Logging
        System.out.println("\n=== 切换到JDK Logging ===");
        LoggerFactory.setLoggerType("jdk");
        Logger logger2 = LoggerFactory.getLogger("com.example.Controller");
        logger2.info("请求处理中");
        logger2.error("请求处理失败");
    }
}

五、开源框架中的应用

5.1 Spring MVC的HandlerAdapter

Spring MVC使用适配器模式支持多种类型的Controller。

java 复制代码
/**
 * Spring MVC的HandlerAdapter接口(简化版)
 * 不同类型的Handler需要不同的适配器来执行
 */
public interface HandlerAdapter {
    /**
     * 判断是否支持该Handler
     */
    boolean supports(Object handler);

    /**
     * 执行Handler
     */
    void handle(Object handler, HttpServletRequest request, HttpServletResponse response)
            throws Exception;
}

/**
 * 简化的HttpServletRequest和Response
 */
class HttpServletRequest {
    private java.util.Map<String, String> parameters = new java.util.HashMap<>();

    public String getParameter(String name) {
        return parameters.get(name);
    }

    public void setParameter(String name, String value) {
        parameters.put(name, value);
    }
}

class HttpServletResponse {
    private String content;

    public void setContent(String content) {
        this.content = content;
    }

    public String getContent() {
        return content;
    }
}

/**
 * 被适配者1:实现Controller接口的处理器
 */
interface Controller {
    String handleRequest(HttpServletRequest request, HttpServletResponse response);
}

class UserController implements Controller {
    @Override
    public String handleRequest(HttpServletRequest request, HttpServletResponse response) {
        System.out.println("[Controller接口] 处理用户请求");
        return "user-view";
    }
}

/**
 * 被适配者2:使用@RequestMapping注解的处理器
 */
class AnnotatedController {
    public String getUser(String id) {
        System.out.println("[注解Controller] 获取用户: " + id);
        return "user-detail";
    }

    public String saveUser(String name) {
        System.out.println("[注解Controller] 保存用户: " + name);
        return "success";
    }
}

/**
 * 适配器1:Controller接口适配器
 */
class SimpleControllerHandlerAdapter implements HandlerAdapter {

    @Override
    public boolean supports(Object handler) {
        return handler instanceof Controller;
    }

    @Override
    public void handle(Object handler, HttpServletRequest request, HttpServletResponse response)
            throws Exception {
        Controller controller = (Controller) handler;
        String viewName = controller.handleRequest(request, response);
        response.setContent("View: " + viewName);
    }
}

/**
 * 适配器2:注解Controller适配器
 */
class RequestMappingHandlerAdapter implements HandlerAdapter {

    @Override
    public boolean supports(Object handler) {
        return handler instanceof AnnotatedController;
    }

    @Override
    public void handle(Object handler, HttpServletRequest request, HttpServletResponse response)
            throws Exception {
        AnnotatedController controller = (AnnotatedController) handler;

        // 简化:根据请求路径调用不同方法
        String uri = request.getParameter("uri");
        String result;

        if ("/getUser".equals(uri)) {
            String id = request.getParameter("id");
            result = controller.getUser(id);
        } else if ("/saveUser".equals(uri)) {
            String name = request.getParameter("name");
            result = controller.saveUser(name);
        } else {
            result = "404";
        }

        response.setContent("Result: " + result);
    }
}

/**
 * DispatcherServlet(简化版)
 */
class DispatcherServlet {

    private java.util.List<HandlerAdapter> handlerAdapters = new java.util.ArrayList<>();

    public DispatcherServlet() {
        // 注册所有适配器
        handlerAdapters.add(new SimpleControllerHandlerAdapter());
        handlerAdapters.add(new RequestMappingHandlerAdapter());
    }

    public void doDispatch(Object handler, HttpServletRequest request, HttpServletResponse response)
            throws Exception {

        // 找到支持该Handler的适配器
        HandlerAdapter adapter = getHandlerAdapter(handler);

        if (adapter == null) {
            throw new Exception("没有找到支持的HandlerAdapter");
        }

        // 使用适配器执行Handler
        adapter.handle(handler, request, response);
    }

    private HandlerAdapter getHandlerAdapter(Object handler) {
        for (HandlerAdapter adapter : handlerAdapters) {
            if (adapter.supports(handler)) {
                return adapter;
            }
        }
        return null;
    }
}

/**
 * 测试Spring MVC适配器
 */
class SpringMVCAdapterDemo {
    public static void main(String[] args) throws Exception {
        DispatcherServlet servlet = new DispatcherServlet();

        // 场景1:使用Controller接口
        System.out.println("=== 场景1:Controller接口 ===");
        Controller controller1 = new UserController();
        HttpServletRequest request1 = new HttpServletRequest();
        HttpServletResponse response1 = new HttpServletResponse();

        servlet.doDispatch(controller1, request1, response1);
        System.out.println("响应: " + response1.getContent());

        // 场景2:使用注解Controller
        System.out.println("\n=== 场景2:注解Controller ===");
        AnnotatedController controller2 = new AnnotatedController();
        HttpServletRequest request2 = new HttpServletRequest();
        request2.setParameter("uri", "/getUser");
        request2.setParameter("id", "123");
        HttpServletResponse response2 = new HttpServletResponse();

        servlet.doDispatch(controller2, request2, response2);
        System.out.println("响应: " + response2.getContent());
    }
}

5.2 Java I/O的InputStreamReader

InputStreamReader是字节流到字符流的适配器。

java 复制代码
import java.io.*;

/**
 * InputStreamReader的适配器模式
 *
 * InputStream (字节流)  →  InputStreamReader  →  Reader (字符流)
 *                            (适配器)
 */
public class InputStreamReaderDemo {

    public static void main(String[] args) throws IOException {
        // InputStream只能读取字节
        InputStream inputStream = new ByteArrayInputStream("Hello 世界".getBytes("UTF-8"));

        // InputStreamReader将字节流适配为字符流
        Reader reader = new InputStreamReader(inputStream, "UTF-8");

        // 现在可以按字符读取(自动处理字符编码)
        int c;
        while ((c = reader.read()) != -1) {
            System.out.print((char) c);
        }

        reader.close();
    }
}

/**
 * 简化的InputStreamReader实现原理
 */
class SimpleInputStreamReader extends Reader {

    private InputStream inputStream;
    private String charset;

    public SimpleInputStreamReader(InputStream inputStream, String charset) {
        this.inputStream = inputStream;
        this.charset = charset;
    }

    @Override
    public int read(char[] cbuf, int off, int len) throws IOException {
        // 读取字节
        byte[] bytes = new byte[len];
        int bytesRead = inputStream.read(bytes);

        if (bytesRead == -1) {
            return -1;
        }

        // 将字节转换为字符(适配过程)
        String str = new String(bytes, 0, bytesRead, charset);
        char[] chars = str.toCharArray();

        // 复制到目标数组
        System.arraycopy(chars, 0, cbuf, off, chars.length);
        return chars.length;
    }

    @Override
    public void close() throws IOException {
        inputStream.close();
    }
}

5.3 SLF4J日志门面

SLF4J通过适配器支持多种日志实现。

java 复制代码
/**
 * SLF4J的适配器模式
 *
 * SLF4J API (统一接口)
 *      ↓
 * ┌────┴────┬────────┬─────────┐
 * │         │        │         │
 * Log4j   Logback  JDK Log  其他...
 * Adapter Adapter  Adapter
 *
 * 用户代码只依赖SLF4J接口,具体实现通过适配器桥接
 */
public class SLF4JAdapterExample {

    // 简化的SLF4J Logger接口
    interface Logger {
        void info(String msg);
        void error(String msg, Throwable t);
    }

    // 简化的LoggerFactory
    static class LoggerFactory {
        public static Logger getLogger(Class<?> clazz) {
            // 根据classpath中的实现选择适配器
            // 这里简化为直接返回Log4j适配器
            return new Log4jLoggerAdapter(clazz.getName());
        }
    }

    // Log4j适配器实现
    static class Log4jLoggerAdapter implements Logger {
        private String name;

        public Log4jLoggerAdapter(String name) {
            this.name = name;
        }

        @Override
        public void info(String msg) {
            // 适配到Log4j的API
            System.out.println("[Log4j-Adapter] INFO " + name + ": " + msg);
        }

        @Override
        public void error(String msg, Throwable t) {
            System.out.println("[Log4j-Adapter] ERROR " + name + ": " + msg);
            if (t != null) {
                t.printStackTrace();
            }
        }
    }

    // 使用示例
    public static void main(String[] args) {
        Logger logger = LoggerFactory.getLogger(SLF4JAdapterExample.class);
        logger.info("应用启动");
        logger.error("发生错误", new Exception("测试异常"));
    }
}

5.4 Arrays.asList() - 数组到List的适配

java 复制代码
import java.util.*;

/**
 * Arrays.asList()将数组适配为List
 */
public class ArraysAsListDemo {

    public static void main(String[] args) {
        // 数组
        String[] array = {"A", "B", "C"};

        // Arrays.asList()将数组适配为List接口
        List<String> list = Arrays.asList(array);

        System.out.println("List大小: " + list.size());
        System.out.println("第一个元素: " + list.get(0));

        // 注意:返回的List是固定大小的,不支持add/remove
        // list.add("D"); // 会抛出UnsupportedOperationException

        // 但支持修改元素
        list.set(0, "AA");
        System.out.println("修改后: " + Arrays.toString(array)); // [AA, B, C]
    }
}

/**
 * 简化的Arrays.asList()实现原理
 */
class ArraysAdapter {

    public static <T> List<T> asList(T[] array) {
        return new ArrayList<>(array);
    }

    // 适配器:将数组包装为List
    static class ArrayList<E> extends AbstractList<E> {
        private final E[] array;

        ArrayList(E[] array) {
            this.array = array;
        }

        @Override
        public E get(int index) {
            return array[index];
        }

        @Override
        public E set(int index, E element) {
            E oldValue = array[index];
            array[index] = element;
            return oldValue;
        }

        @Override
        public int size() {
            return array.length;
        }
    }
}

六、适配器模式的优缺点

6.1 优点

1. 提高类的复用性

scss 复制代码
无需修改现有类:
┌─────────────┐
│ ExistingClass│  (不变)
└─────────────┘
       ↑
       │适配
┌──────┴──────┐
│   Adapter   │  (新增)
└─────────────┘
       ↓
  新的接口使用

2. 增加类的透明性

  • 客户端不需要知道适配的细节
  • 只需要知道目标接口

3. 灵活性好

  • 可以随时替换适配器
  • 不影响客户端代码

4. 符合开闭原则

  • 添加新的适配器无需修改现有代码

6.2 缺点

1. 过多使用会使系统凌乱

复制代码
如果有10个不同的被适配者:
→ 需要10个适配器
→ 系统结构复杂

2. 增加系统复杂度

  • 引入额外的类和对象
  • 增加代码量

3. 类适配器的局限性

  • Java不支持多重继承
  • 只能适配一个被适配者

七、适配器模式最佳实践

7.1 优先使用对象适配器

java 复制代码
/**
 * 推荐:对象适配器(组合)
 */
public class GoodAdapter implements Target {
    private Adaptee adaptee;  // 使用组合

    public GoodAdapter(Adaptee adaptee) {
        this.adaptee = adaptee;
    }

    @Override
    public void request() {
        adaptee.specificRequest();
    }
}

/**
 * 不推荐:类适配器(继承)
 * 缺点:
 * 1. 破坏封装性
 * 2. 只能适配一个类
 * 3. 继承关系固定
 */
public class NotRecommendedAdapter extends Adaptee implements Target {
    @Override
    public void request() {
        specificRequest();
    }
}

7.2 使用接口适配器简化实现

java 复制代码
/**
 * 场景:接口有很多方法,但大多数时候只需要实现其中几个
 */
interface WindowListener {
    void windowOpened();
    void windowClosing();
    void windowClosed();
    void windowIconified();
    void windowDeiconified();
    void windowActivated();
    void windowDeactivated();
}

/**
 * 接口适配器:提供默认空实现
 */
abstract class WindowAdapter implements WindowListener {
    @Override
    public void windowOpened() {}

    @Override
    public void windowClosing() {}

    @Override
    public void windowClosed() {}

    @Override
    public void windowIconified() {}

    @Override
    public void windowDeiconified() {}

    @Override
    public void windowActivated() {}

    @Override
    public void windowDeactivated() {}
}

/**
 * 使用者只需要重写需要的方法
 */
class MyWindowListener extends WindowAdapter {
    @Override
    public void windowClosing() {
        System.out.println("窗口正在关闭");
        // 只实现这一个方法
    }
}

7.3 适配器与工厂模式结合

java 复制代码
/**
 * 适配器工厂:统一管理适配器的创建
 */
public class AdapterFactory {

    private static java.util.Map<String, Class<? extends PaymentProcessor>> adapters =
            new java.util.HashMap<>();

    static {
        adapters.put("alipay", AlipayAdapter.class);
        adapters.put("wechat", WeChatPayAdapter.class);
        adapters.put("unionpay", UnionPayAdapter.class);
    }

    /**
     * 根据类型创建适配器
     */
    public static PaymentProcessor createAdapter(String type) {
        Class<? extends PaymentProcessor> clazz = adapters.get(type);

        if (clazz == null) {
            throw new IllegalArgumentException("不支持的支付类型: " + type);
        }

        try {
            return clazz.getDeclaredConstructor().newInstance();
        } catch (Exception e) {
            throw new RuntimeException("创建适配器失败", e);
        }
    }

    /**
     * 注册新的适配器
     */
    public static void registerAdapter(String type, Class<? extends PaymentProcessor> adapterClass) {
        adapters.put(type, adapterClass);
    }
}

7.4 双向适配器

java 复制代码
/**
 * 双向适配器:可以在两个接口之间相互适配
 */
public class TwoWayAdapter implements Target, Adaptee {

    private Target target;
    private Adaptee adaptee;

    public TwoWayAdapter(Target target) {
        this.target = target;
    }

    public TwoWayAdapter(Adaptee adaptee) {
        this.adaptee = adaptee;
    }

    // 实现Target接口
    @Override
    public void request() {
        if (adaptee != null) {
            adaptee.specificRequest();
        }
    }

    // 实现Adaptee接口
    @Override
    public void specificRequest() {
        if (target != null) {
            target.request();
        }
    }
}

interface Target {
    void request();
}

interface Adaptee {
    void specificRequest();
}

八、总结

8.1 核心要点

  1. 适配器模式的本质:将一个接口转换成另一个接口,使不兼容的类可以一起工作
  2. 三种形式
    • 类适配器(继承)
    • 对象适配器(组合,推荐)
    • 接口适配器(缺省实现)
  3. 适用场景
    • 系统需要使用现有的类,但接口不符
    • 想创建一个可重用的类,与不相关的类协同工作
    • 需要统一多个第三方库的接口

8.2 使用建议

arduino 复制代码
选择适配器模式的检查清单:

✓ 是否需要使用现有类,但接口不匹配?
✓ 是否需要整合多个第三方库?
✓ 是否想在不修改源代码的情况下使用某个类?
✓ 是否需要为一组相似的类提供统一接口?

如果以上都是"是",那么适配器模式是个好选择!

8.3 与其他模式的对比

diff 复制代码
适配器 vs 装饰者:
- 适配器:改变接口
- 装饰者:增强功能,不改变接口

适配器 vs 代理:
- 适配器:接口转换
- 代理:控制访问,接口相同

适配器 vs 外观模式:
- 适配器:转换一个接口
- 外观:简化多个接口

8.4 实践原则

  1. 优先使用对象适配器:更灵活,支持运行时替换
  2. 保持适配器简单:只做接口转换,不添加额外逻辑
  3. 结合工厂模式:统一管理适配器的创建
  4. 文档化:清楚说明适配的源接口和目标接口

相关推荐
用户84913717547161 小时前
生产级故障排查实战:从制造 OOM 到 IDEA Profiler 深度破案
java·jvm
雨中飘荡的记忆1 小时前
深入理解设计模式之装饰者模式
java·设计模式
雨中飘荡的记忆1 小时前
秒杀系统设计与实现
java·redis·lua
小坏讲微服务2 小时前
Spring Cloud Alibaba 整合 Scala 教程完整使用
java·开发语言·分布式·spring cloud·sentinel·scala·后端开发
老鼠只爱大米2 小时前
Java设计模式之外观模式(Facade)详解
java·设计模式·外观模式·facade·java设计模式
qq_172805592 小时前
Go 语言结构型设计模式深度解析
开发语言·设计模式·golang
vx_dmxq2112 小时前
【微信小程序学习交流平台】(免费领源码+演示录像)|可做计算机毕设Java、Python、PHP、小程序APP、C#、爬虫大数据、单片机、文案
java·spring boot·python·mysql·微信小程序·小程序·idea
9号达人2 小时前
优惠系统演进:从"实时结算"到"所见即所得",前端传参真的鸡肋吗?
java·后端·面试
AAA简单玩转程序设计2 小时前
Java进阶小妙招:ArrayList和LinkedList的"相爱相杀"
java