模式组合应用-适配器模式

写在前面

Hello,我是易元,这篇文章是我学习设计模式时的笔记和心得体会。如果其中有错误,欢迎大家留言指正!

该部分为各模式组合使用,涉及代码较多,熟能生巧。


内容回顾

定义

适配器模式是一种结构型设计模式,它允许接口不兼容的类能够相互合作。

适配器模式通过创建一个中间层(适配器)来连接两个不兼容的接口,使得原本由于接口不匹配而不能一起工作的类可以协同工作。

它的主要目的是将一个类的接口转换成客户期望的另一个接口,从而解决接口不匹配的问题从而实现代码的复用

核心思想

  • 转化: 将一个类的接口转化成客户期望的另一个接口,使得客户端可以透明的使用被适配者。
  • 桥接: 在不修改现有代码(特别是被适配者代码)的情况下,使不兼容的接口能够协同工作,这对集成第三方库、遗留系统或未来可能变化的接口尤为重要。

实现方案

类适配器模式
  • 实现方式: 类适配器通过继承被适配者类,同时实现目标接口来完成适配。适配器类既是被适配者,又是目标接口的实现者。

  • 优点: 实现简单,直接继承被适配者的功能。

  • 缺点:

    1. 由于Java单继承的限制,适配器类只能继承一个被适配者类,这限制了其灵活性。
    2. 适配器与被适配者之间的耦合度较高,当被适配者发生变化时,适配器也可能需要修改。
    3. 无法适配被适配者的子类。
  • 适用场景: 当被适配者是一个类,且不需要适配其子类时。

对象适配器模式
  • 实现方式: 对象适配器通过组合(持有引用)被适配者对象,并实现目标接口来完成适配。适配器类将客户端的请求委派给内部持有的被适配者对象。

  • 优点:

    1. 适配器可以与任何实现了被适配者接口的类一起工作,也可以适配被适配者的子类。
    2. 适配器与被适配者之间的耦合度较低,被适配者的变化对适配器的影响较小。
    3. 一个适配器可以适配多个被适配者(通过组合多个被适配者对象)。
  • 缺点: 与类适配器稍微复杂一些,需要额外的对象引用。

  • 适用场景: 场景适用较多,特别是当被适配者是接口或抽象类时,或者需要适配多个被适配者时。

使用时机

  1. 接口不兼容: 最核心的触发条件,当适用某个现有类或第三方库,但其接口与你当前系统期望的接口不匹配时
  2. 遗留系统集成: 在企业级应用中,新系统往往需要与历史悠久的遗留系统进行交互,遗留系统通常有独特的数据格式和接口,适配器模式可以作为新旧系统之间的桥梁,实现平滑过渡和数据转换。
  3. 统一接口: 当有多个功能相似但接口不同的类时,可以使用适配器模式为它们提供一个统一的接口,方便客户端调用。

模式组合的应用

适配器模式回顾

假设有一个旧的多媒体播放器系统,最初的设计是为了播放MP3格式的音频文件。现在,随着技术的发展和用户需求的变化,我们需要扩展这个播放器 使其能够播放MP4视频文件和VLC视频文件,而又不想修改现有MP3播放器的代码。采用适配器模式解决该问题。

MediaPlayer(目标接口)
typescript 复制代码
public interface MediaPlayer {

    /**
     * 播放媒体文件的方法
     *
     * @param audioType 媒体类型
     * @param fileName  文件名
     */
    void play(String audioType, String fileName);

}
Mp4Player(被适配者)
typescript 复制代码
public class Mp4Player {

    /**
     * 播放MP4文件的方法
     * <p>
     * 此方法名和参数与 MediaPlayer 接口不兼容。
     *
     * @param fileName
     */
    public void playMp4(String fileName) {
        System.out.println("Playing mp4 file. Name: " + fileName);
    }

}
VlcPlayer(被适配者)
typescript 复制代码
public class VlcPlayer {

    /**
     * 播放 vlc 文件的方法
     * <p>
     * 此方法名和参数与 MediaPlayer 接口不兼容
     *
     * @param fileName VLC文件名
     */
    public void playVlc(String fileName) {
        System.out.println("Playing vlc file. Name: " + fileName);
    }

}
MediaAdapter(适配器类)
arduino 复制代码
public class MediaAdapter implements MediaPlayer {

    /**
     * 持有被适配者对象的引用
     */
    private Mp4Player mp4Player;

    /**
     * 持有被适配者对象的引用
     */
    private VlcPlayer vlcPlayer;

    /**
     * 构造函数,根据媒体类型初始化响应的播放器
     *
     * @param audioType 媒体类型
     */
    public MediaAdapter(String audioType) {

        if (audioType.equalsIgnoreCase("mp4")) {
            mp4Player = new Mp4Player();
        }
        else if (audioType.equalsIgnoreCase("vlc")) {
            vlcPlayer = new VlcPlayer();
        }

    }

    @Override
    public void play(String audioType, String fileName) {

        // 根据媒体类型,调用相应被适配者的播放方法
        if (audioType.equalsIgnoreCase("mp4")) {
            mp4Player.playMp4(fileName);
        }
        else if (audioType.equalsIgnoreCase("vlc")) {
            vlcPlayer.playVlc(fileName);
        }
        else {
            System.out.println("Invalid media. " + audioType + " format not supported");
        }

    }

}
AudioPlayer(原始播放器)
typescript 复制代码
public class AudioPlayer implements MediaPlayer {

    /**
     * 适配器类引用
     */
    private MediaAdapter mediaAdapter;

    @Override
    public void play(String audioType, String fileName) {

        if (audioType.equalsIgnoreCase("mp3")) {
            System.out.println("Playing mp3 file. Name: " + fileName);
        }

        // mediaAdapter提供了播放其他文件格式的支持
        else if (audioType.equalsIgnoreCase("vlc") || audioType.equalsIgnoreCase("mp4")) {
            mediaAdapter = new MediaAdapter(audioType);
            mediaAdapter.play(audioType, fileName);
        }
        else {
            System.out.println("Invalid media. " + audioType + " format not supported");
        }
    }
}
测试类
java 复制代码
    @Test
    public void test_player() {

        AudioPlayer audioPlayer = new AudioPlayer();

        audioPlayer.play("mp3", "beyond the horizon.mp3");
        audioPlayer.play("mp4", "alone.mp4");
        audioPlayer.play("vlc", "far far away.vlc");
        audioPlayer.play("avi", "mind me.avi");

    }
执行结果
lua 复制代码
Playing mp3 file. Name: beyond the horizon.mp3
Playing mp4 file. Name: alone.mp4
Playing vlc file. Name: far far away.vlc
Invalid media. avi format not supported

Process finished with exit code 0
  • 以上为复习适配者模式的使用加深记忆

适配器模式+工厂模式

在多媒体播放器示例中, AudioPlayer 在需要播放 VLC 或者 MP4 文件时,直接在 play 方法中通过 new MediaAdapter(audioType)的方式创建 MediaAdapter 实例,如果未来需要支持更多媒体类型,AudioPlayer 的 play 方法中的条件判断逻辑会变得越来越复杂和臃肿。通过引入工厂模式,可以将适配器的创建逻辑集中管理,使得 AudioPlayer 无需关心适配器的具体创建过程。

MediaAdapterFactory(多媒体适配器工厂)
arduino 复制代码
public interface MediaAdapterFactory {

    MediaPlayer createAdapter(String audioType);

}
ConcreteMediaAdapterFactory(具体工厂类)
typescript 复制代码
public class ConcreteMediaAdapterFactory implements MediaAdapterFactory {

    @Override
    public MediaPlayer createAdapter(String audioType) {
        if (audioType.equalsIgnoreCase("mp4") || audioType.equalsIgnoreCase("vlc")) {
            return new MediaAdapter(audioType);
        }

        return null;
    }
}
AudioPlayer(原接口)
typescript 复制代码
public class AudioPlayer implements MediaPlayer {

    private MediaAdapterFactory adapterFactory;

    /**
     * 构造函数 初始化 适配器工厂类
     *
     * @param adapterFactory
     */
    public AudioPlayer(MediaAdapterFactory adapterFactory) {
        this.adapterFactory = adapterFactory;
    }

    @Override
    public void play(String audioType, String fileName) {

        if (audioType.equalsIgnoreCase("mp3")) {
            System.out.println("Playing mp3 file. Name: " + fileName);
        }
        else {
            MediaPlayer adapter = adapterFactory.createAdapter(audioType);

            if (adapter != null) {
                adapter.play(audioType, fileName);
            }
            else {
                System.out.println("Invalid media. " + audioType + " format not supported");
            }
        }

    }
}
  • AudioPlayer 中添加对工厂的引用,修改判断逻辑,直接通过工厂方法获取适配器类。
测试类
java 复制代码
    @Test
    public void test_player() {
        MediaAdapterFactory factory = new ConcreteMediaAdapterFactory();
        AudioPlayer audioPlayer = new AudioPlayer(factory);

        audioPlayer.play("mp3", "beyond the horizon.mp3");
        audioPlayer.play("mp4", "alone.mp4");
        audioPlayer.play("vlc", "far far away.vlc");
        audioPlayer.play("avi", "mind me.avi");
    }
执行结果
lua 复制代码
Playing mp3 file. Name: beyond the horizon.mp3
Playing mp4 file. Name: alone.mp4
Playing vlc file. Name: far far away.vlc
Invalid media. avi format not supported

Process finished with exit code 0

适配器模式+桥接模式

假设我们需要开发一个电商支付系统,需要支持多种支付方式(信用卡、支付宝、微信等), 同时这些支付方式可能有不同的接口版本(如 支付宝有新版和旧版API等)

组合思想
  • 桥接模式旨在将抽象与实现分离,使得它们可以独立变化。
  • 适配器模式则用于解决接口不兼容的问题。
  • 当桥接模式的抽象层需要与一个不兼容的实现层进行交互时,适配器模式可以作为桥梁,将不兼容的实现层适配到桥接模式所期望的接口。
旧版本API
arduino 复制代码
/**
 * 旧版微信支付接口
 * <p>
 * 与 PaymentProcessor 不兼容
 */
public interface LegacyWeChatPay {

    boolean pay(double amount, String openId);

    boolean cancelPayment(String paymentNo);

}


/**
 * 旧版微信支付实现类
 */
public class LegacyWeChatPayImpl implements LegacyWeChatPay {

    @Override
    public boolean pay(double amount, String openId) {

        System.out.println(String.format("旧版微信支付: 金额 %.2f, OpenID %s\n", amount, openId));

        return true;
    }

    @Override
    public boolean cancelPayment(String paymentNo) {

        System.out.println(String.format("旧版微信支付取消: 支付单号 %s\n", paymentNo));

        return true;
    }
}
PaymentProcessor
arduino 复制代码
public interface PaymentProcessor {

    boolean processPayment(double amount, Map<String, String> paymentDetails);

    boolean refund(double amount, String transactionId);
}
  • PaymentProcessor 为桥接实现部分,定义支付处理通用接口。
实现部分具体实现类
typescript 复制代码
public class AliPayProcessor implements PaymentProcessor {

    @Override
    public boolean processPayment(double amount, Map<String, String> paymentDetails) {

        System.out.println(
                String.format("支付宝支付: 金额 %.2f, 支付宝账号 %s\n",
                        amount, paymentDetails.get("aliPayAccount"))
        );

        return true;
    }

    @Override
    public boolean refund(double amount, String transactionId) {

        System.out.println(String.format("支付宝退款: 金额 %.2f, 交易ID %s\n", amount, transactionId));

        return true;
    }
}

public class CreditCardProcessor implements PaymentProcessor {

    @Override
    public boolean processPayment(double amount, Map<String, String> paymentDetails) {

        System.out.println(String.format("信用卡支付: 金额 %.2f, 卡号 %s, 有效期 %s\n",
                amount, paymentDetails.get("cardNumber"), paymentDetails.get("expiryDate")));

        return true;
    }

    @Override
    public boolean refund(double amount, String transactionId) {

        System.out.println(String.format("信用卡退款: 金额 %.2f, 支付宝账号 %s \n", amount, transactionId));

        return true;
    }
}
  • 实现部分具体实现类,包含 AliPayProcessorCreditCardProcessor,定义各自具体支付逻辑。
Payment
csharp 复制代码
public abstract class Payment {

    /**
     * 接口部分的引用
     */
    protected PaymentProcessor paymentProcessor;

    protected Payment(PaymentProcessor paymentProcessor) {
        this.paymentProcessor = paymentProcessor;
    }

    public abstract boolean makePayment(double amount);

    public abstract boolean requestRefund(double amount);

    public String getTransactionId() {
        return "TXN_" + System.currentTimeMillis();
    }
}
  • Payment 支付抽象类,桥接模式的抽象部分
抽象具体部分具体实现类
typescript 复制代码
/**
 * 支付宝支付
 * <p>
 * 抽象子类
 */
public class AliPayPayment extends Payment {

    private String aliPayAccount;

    public AliPayPayment(PaymentProcessor paymentProcessor, String aliPayAccount) {
        super(paymentProcessor);
        this.aliPayAccount = aliPayAccount;
    }

    @Override
    public boolean makePayment(double amount) {

        Map<String, String> details = new HashMap<>();
        details.put("aliPayAccount", aliPayAccount);

        return paymentProcessor.processPayment(amount, details);
    }

    @Override
    public boolean requestRefund(double amount) {
        return paymentProcessor.refund(amount, super.getTransactionId());
    }
}

/**
 * 信用卡支付
 * <p>
 * 抽象子类
 */
public class CreditCardPayment extends Payment {

    private String cardNumber;

    private String expiryDate;

    private String cvv;

    public CreditCardPayment(PaymentProcessor paymentProcessor, String cardNumber, String expiryDate, String cvv) {
        super(paymentProcessor);
        this.cardNumber = cardNumber;
        this.expiryDate = expiryDate;
        this.cvv = cvv;
    }

    @Override
    public boolean makePayment(double amount) {

        Map<String, String> details = new HashMap<>();
        details.put("cardNumber", cardNumber);
        details.put("expiryDate", expiryDate);
        details.put("cvv", cvv);

        return paymentProcessor.processPayment(amount, details);
    }

    @Override
    public boolean requestRefund(double amount) {
        return paymentProcessor.refund(amount, super.getTransactionId());
    }
}
WeChatPayAdapter
typescript 复制代码
/**
 * 适配器类
 * <p>
 * 将 LegacyWeChatPay 适配道 PaymentProcessor 接口
 */
public class WeChatPayAdapter implements PaymentProcessor {

    /**
     * 原支付接口的引用
     */
    private LegacyWeChatPay legacyWeChatPay;

    public WeChatPayAdapter(LegacyWeChatPay legacyWeChatPay) {
        this.legacyWeChatPay = legacyWeChatPay;
    }

    @Override
    public boolean processPayment(double amount, Map<String, String> paymentDetails) {
        return legacyWeChatPay.pay(amount, paymentDetails.get("openId"));
    }

    @Override
    public boolean refund(double amount, String transactionId) {
        // 旧版本中,不支持退款,仅支持取消支付。
        return legacyWeChatPay.cancelPayment(transactionId);
    }
}
  • 适配器类,实现了 PaymentProcessor 接口部分,并持有 LegacyWeChatPay 实例的引用,在通用接口中调整数据结构,调用旧版支付。
WeChatPayment
typescript 复制代码
/**
 * 微信支付类
 * <p>
 * 抽象子类
 */
public class WeChatPayment extends Payment {

    private String openId;

    public WeChatPayment(PaymentProcessor paymentProcessor, String openId) {
        super(paymentProcessor);
        this.openId = openId;
    }

    @Override
    public boolean makePayment(double amount) {

        Map<String, String> details = new HashMap<>();
        details.put("openId", openId);
        return paymentProcessor.processPayment(amount, details);

    }

    @Override
    public boolean requestRefund(double amount) {
        System.out.println("注意: 微信旧版支付只能取消支付,不能部分退款");
        return paymentProcessor.refund(amount, getTransactionId());
    }
}
  • 新增抽象部分实现,用于调用适配器类,执行旧版本支付逻辑。
测试类
ini 复制代码
    @Test
    public void test_pay() {

        // 创建不同的支付处理器
        PaymentProcessor creditCardProcessor = new CreditCardProcessor();
        PaymentProcessor aliPayProcessor = new AliPayProcessor();

        // 创建旧版微信支付实例和适配器
        PaymentProcessor weChatPayAdapter = new WeChatPayAdapter(new LegacyWeChatPayImpl());
        Payment creditCardPayment = new CreditCardPayment(creditCardProcessor, "1234-5678-9012-3456", "12/25", "123");

        Payment aliPayPayment = new AliPayPayment(aliPayProcessor, "user@alipay.com");
        Payment weChatPayment = new WeChatPayment(weChatPayAdapter, "oUpF8uMuAJO_M2pxb1Q9zNjWeS6o");

        // 支付
        System.out.println("--- 支付测试 ---");
        creditCardPayment.makePayment(100.0);
        aliPayPayment.makePayment(200.0);
        weChatPayment.makePayment(150.0);

        // 退款
        System.out.println("--- 退款测试 ---");
        creditCardPayment.requestRefund(50.0);
        aliPayPayment.requestRefund(100.0);
        weChatPayment.requestRefund(150.0);

        // 切换支付处理器
        System.out.println("--- 切换支付处理器测试 ---");
        Payment creditCardWithAliPayProcessor = new CreditCardPayment(aliPayProcessor, "1234-5678-9012-3456", "12/25", "123");
        creditCardWithAliPayProcessor.makePayment(300.0);

    }
执行结果
makefile 复制代码
--- 支付测试 ---
信用卡支付: 金额 100.00, 卡号 1234-5678-9012-3456, 有效期 12/25

支付宝支付: 金额 200.00, 支付宝账号 user@alipay.com

旧版微信支付: 金额 150.00, OpenID oUpF8uMuAJO_M2pxb1Q9zNjWeS6o

--- 退款测试 ---
信用卡退款: 金额 50.00, 支付宝账号 TXN_1754707835276 

支付宝退款: 金额 100.00, 交易ID TXN_1754707835276

注意: 微信旧版支付只能取消支付,不能部分退款
旧版微信支付取消: 支付单号 TXN_1754707835276

--- 切换支付处理器测试 ---
支付宝支付: 金额 300.00, 支付宝账号 null


Process finished with exit code 0
组合优势
  • 添加新的支付方式只需要继承Payment类
  • 添加新的支付处理器只需要实现PaymentProcessor接口
  • 集成旧系统只需要创建适配器,不影响核心架构

适配器模式+策略模式

依然以支付系统为例,将 适配器模式 与 策略模式组合使用。

代码结构

代码结构

ThirdPartyPayment
arduino 复制代码
public interface ThirdPartyPayment {

    void makePayment(double amount, String account);

}
  • ThirdPartyPayment 为第三方支付接口,为原始定义接口,现需要适配该接口。
被适配目标类
typescript 复制代码
/**
 * 支付宝实现
 * <p>
 * 需适配的类
 *
 * @author YiYuan
 * @data 2025/8/8
 * @apoNote
 */
public class AliPayService implements ThirdPartyPayment {

    @Override
    public void makePayment(double amount, String account) {
        System.out.println(String.format("AliPay payment processed: $%.2f to account %s \n", amount, account));
    }

}

/**
 * 微信支付
 * <p>
 * 需适配的类
 *
 * @author YiYuan
 * @data 2025/8/8
 * @apoNote
 */
public class WeChatPayService implements ThirdPartyPayment {

    @Override
    public void makePayment(double amount, String account) {

        System.out.println(String.format("WeChatPay payment processed: $%.2f to account %s \n", amount, account));

    }

}
UnifiedPayment
arduino 复制代码
/**
 * 统一的支付目标接口
 * <p>
 * 适配器模式的目标接口
 */
public interface UnifiedPayment {

    void processPayment(double amount, String paymentDetails);

}
  • UnifiedPayment 是支付系统内,新定义的支付接口类,所有支付均需要对该类进行实现,适配目标类
支付系统实现类
typescript 复制代码
/**
 * PayPal 支付
 */
public class PayPalPayment implements UnifiedPayment {

    private String email;

    public PayPalPayment(String email) {
        this.email = email;
    }

    @Override
    public void processPayment(double amount, String paymentDetails) {

        System.out.println(String.format("Processing PayPal payment: $%.2f\n", amount));
        System.out.println("PayPal account: " + email);
        System.out.println("Payment details: " + paymentDetails);

    }

}

/**
 * 信用卡支付
 */
public class CreditCardPayment implements UnifiedPayment {

    private String cardNumber;

    private String cardHolder;

    public CreditCardPayment(String cardNumber, String cardHolder) {
        this.cardNumber = cardNumber;
        this.cardHolder = cardHolder;
    }

    @Override
    public void processPayment(double amount, String paymentDetails) {

        System.out.println(String.format("Processing credit card payment: $%.2f\n", amount));
        System.out.println("Card: " + maskCardNumber(cardNumber) + ", Holder: " + cardHolder);
        System.out.println("Payment details: " + paymentDetails);

    }

    private String maskCardNumber(String cardNumber) {
        return "****_****_****_" + cardNumber.substring(cardNumber.length() - 4);
    }

}
  • CreditCardPayment,PayPalPayment为新系统的支付实现类。
arduino 复制代码
/**
 * 旧系统支付适配器
 */
public class ThirdPartyPaymentAdapter implements UnifiedPayment {

    private ThirdPartyPayment thirdPartyPayment;

    private String account;

    public ThirdPartyPaymentAdapter(ThirdPartyPayment thirdPartyPayment, String account) {
        this.thirdPartyPayment = thirdPartyPayment;
        this.account = account;
    }

    @Override
    public void processPayment(double amount, String paymentDetails) {

        System.out.println("Processing third party payment - " + paymentDetails);
        thirdPartyPayment.makePayment(amount, account);

    }

}
  • ThirdPartyPaymentAdapter 为旧支付系统适配类,对新支付接口类 UnifiedPayment 进行了实现,并持有旧支付系统 ThirdPartyPayment 的引用。
PaymentProcessor
typescript 复制代码
public class PaymentProcessor {

    private UnifiedPayment paymentMethod;

    private static Map<String, UnifiedPayment> paymentMethods = new HashMap<>();

    public PaymentProcessor(UnifiedPayment paymentMethod) {
        this.paymentMethod = paymentMethod;
    }

    public void executePayment(double amount, String details) {
        paymentMethod.processPayment(amount, details);
    }

    // 注册支付方法
    public static void registerPaymentMethod(String name, UnifiedPayment method) {
        paymentMethods.put(name, method);
    }

    // 获取支付方法
    public static UnifiedPayment getPaymentMethod(String name) {
        return paymentMethods.get(name);
    }


}

public class PaymentProcessorUtil {

    public static void processOrderPayment(double amount, String method, String details) {
        System.out.println("\n=== Processing order payment ===");
        UnifiedPayment paymentMethod = PaymentProcessor.getPaymentMethod(method);

        if (paymentMethod != null) {
            new PaymentProcessor(paymentMethod).executePayment(amount, details);
        }
        else {
            System.out.println("Error: Payment method '" + method + "' not found!");
        }
    }

}
  • PaymentProcessor 为支付策略处理器,通过支付方法的不同,引用不同的支付模块执行逻辑。PaymentProcessorUtil 为其工具类。
测试类
typescript 复制代码
    @Test
    public void test_pay() {

        // 注册支付方式
        PaymentProcessor.registerPaymentMethod("credit", new CreditCardPayment("1234567812345678", "John Doe"));
        PaymentProcessor.registerPaymentMethod("paypal", new PayPalPayment("john.doe@example.com"));
        PaymentProcessor.registerPaymentMethod("alipay", new ThirdPartyPaymentAdapter(new AliPayService(), "alipay_123"));
        PaymentProcessor.registerPaymentMethod("wechat", new ThirdPartyPaymentAdapter(new WeChatPayService(), "wechat_456"));

        PaymentProcessorUtil.processOrderPayment(150.75, "credit", "Order #1001 - Laptop");
        PaymentProcessorUtil.processOrderPayment(89.99, "paypal", "Order #1002 - Mouse");
        PaymentProcessorUtil.processOrderPayment(299.00, "alipay", "Order #1003 - Monitor");
        PaymentProcessorUtil.processOrderPayment(54.99, "wechat", "Order #1004 - Keyboard");

    }
执行结果
ini 复制代码
=== Processing order payment ===
Processing credit card payment: $150.75

Card: ****_****_****_5678, Holder: John Doe
Payment details: Order #1001 - Laptop

=== Processing order payment ===
Processing PayPal payment: $89.99

PayPal account: john.doe@example.com
Payment details: Order #1002 - Mouse

=== Processing order payment ===
Processing third party payment - Order #1003 - Monitor
AliPay payment processed: $299.00 to account alipay_123 


=== Processing order payment ===
Processing third party payment - Order #1004 - Keyboard
WeChatPay payment processed: $54.99 to account wechat_456 


Process finished with exit code 0

适配器模式+装饰器模式

下面将展示一个结合适配器模式和装饰器模式的通用数据读取系统案例。适配器用于整合不同数据源,装饰器用于动态添加功能增强。


DataReader
csharp 复制代码
/**
 * 统一数据读取接口
 */
public interface DataReader {

    String read() throws IOException;

    String getSourceInfo();

}
  • DataReader 读取数据的统一接口,为适配目标接口
DatabaseAccessor
arduino 复制代码
/**
 * 数据库读取类
 */
public class DatabaseAccessor {

    public String fetchDataFromDB(String query) {
        return "Database result for query: " + query;
    }

}
  • DatabaseAccessor 为被适配类,用于在数据库中读取数据。
DatabaseReaderAdapter
arduino 复制代码
/**
 * 数据库读取适配器
 */
public class DatabaseReaderAdapter implements DataReader {

    private final DatabaseAccessor databaseAccessor;

    private final String query;

    public DatabaseReaderAdapter(DatabaseAccessor databaseAccessor, String query) {
        this.databaseAccessor = databaseAccessor;
        this.query = query;
    }

    @Override
    public String read() throws IOException {
        return databaseAccessor.fetchDataFromDB(query);
    }

    @Override
    public String getSourceInfo() {
        return "Database query: " + query;
    }
}
  • DatabaseReaderAdapter 适配器类,持有 DatabaseAccessor 原数据读取接口的引用。
DataReaderDecorator
java 复制代码
/**
 * 基础装饰器类
 */
public abstract class DataReaderDecorator implements DataReader {

    protected final DataReader wrappedReader;

    public DataReaderDecorator(DataReader wrappedReader) {
        this.wrappedReader = wrappedReader;
    }

    @Override
    public String read() throws IOException {
        return wrappedReader.read();
    }

    @Override
    public String getSourceInfo() {
        return wrappedReader.getSourceInfo();
    }
}
  • DataReaderDecorator 基础装饰类,持有 DataReader 接口的引用,并在方法实现中调用 装饰类的 接口方法。
装饰器实现类
kotlin 复制代码
/**
 * 压缩装饰器
 */
public class CompressionDecorator extends DataReaderDecorator {

    public CompressionDecorator(DataReader wrappedReader) {
        super(wrappedReader);
    }

    @Override
    public String read() throws IOException {

        String originalData = wrappedReader.read();
        return this.compress(originalData);

    }

    private String compress(String data) {
        return "Compressed(" + data.length() + " chars): " + data.substring(0, Math.min(20, data.length())) + "...";
    }

    @Override
    public String getSourceInfo() {
        return "[Compressed] " + wrappedReader.getSourceInfo();
    }
}

/**
 * 缓存装饰器
 */
public class CachingDecorator extends DataReaderDecorator {

    private String cache;

    public CachingDecorator(DataReader wrappedReader) {
        super(wrappedReader);
    }

    @Override
    public String read() throws IOException {

        if (cache == null) {
            System.out.println("Cache miss - reading from source");
            cache = wrappedReader.read();
        }
        else {
            System.out.println("Cache hit - returning cached data");
        }

        return cache;
    }

    @Override
    public String getSourceInfo() {
        return "[Cached] " + wrappedReader.getSourceInfo();
    }
}
  • CompressionDecorator,CachingDecorator为装饰器实现类,用于增强原有数据读取类,增加额外功能。
测试类
csharp 复制代码
    @Test
    public void test_read() throws IOException {

        DataReader dbReader = new DatabaseReaderAdapter(new DatabaseAccessor(), "SELECT * FROM users");

        System.out.println("\n=== Basic Adapter Usage ===");
        System.out.println(dbReader.getSourceInfo() + ": " + dbReader.read());

        System.out.println("\n=== Adapter + Decorator Combination ===");

        DataReader cachedDbReader = new CachingDecorator(dbReader);
        System.out.println("First read: " + cachedDbReader.read());
        System.out.println("Second read: " + cachedDbReader.read());

        DataReader superReader = new CompressionDecorator(new CachingDecorator(new EncryptionDecorator(new FileReaderAdapter(new LegacyFileReader(), "D:\sample.txt"))));
        System.out.println("\n=== Super Reader ===");
        System.out.println(superReader.getSourceInfo());
        System.out.println("Data: " + superReader.read());
    }
运行结果
sql 复制代码
=== Basic Adapter Usage ===
Database query: SELECT * FROM users: Database result for query: SELECT * FROM users

=== Adapter + Decorator Combination ===
Cache miss - reading from source
First read: Database result for query: SELECT * FROM users
Cache hit - returning cached data
Second read: Database result for query: SELECT * FROM users

=== Super Reader ===
[Compressed] [Cached] [Encrypted] File: D:\sample.txt
Cache miss - reading from source
Data: Compressed(16 chars): MTIzMzIxMTIzMzIx...

Process finished with exit code 0

适配器模式+外观模式

下面将式和外观模式的跨平台文件管理系统案例。这个案例具有普遍性,适用于需要整合不同系统API并提供简化接口的场景。


FileSystem
arduino 复制代码
/**
 * 统一文件操作接口
 */
public interface FileSystem {

    void createFile(String path);

    void deleteFile(String path);

    String readFile(String path);

    void writeFile(String path, String content);

    String getFileInfo(String path);
}
  • FileSystem 适配目标类
被适配类
typescript 复制代码
/**
 * Windows 文件系统
 * <p>
 * 被适配对象
 */
public class WindowsNativeFileSystem {

    public void createNewFile(String path) {
        System.out.println("Windows: Creating file at " + path);
    }

    public void removeFile(String path) {
        System.out.println("Windows: Deleting file at " + path);
    }

    public String readFileContent(String path) {
        return "Windows file content from " + path;
    }

    public void writeToFile(String path, String content) {
        System.out.println("Windows: Writing to file at " + path + ", content: " + content);
    }

    public String getFileMetadata(String path) {
        return "Windows file info: " + path + ", size: 1024 bytes";
    }
}

/**
 * Linux 文件操作
 * <p>
 * 被适配对象类
 */
public class LinuxNativeFileOperations {

    public void touchFile(String path) {
        System.out.println("Linux: Creating file at " + path);
    }

    public void rmFile(String path) {
        System.out.println("Linux: Removing file at " + path);
    }

    public String catFile(String path) {
        return "Linux file content from " + path;
    }

    public void echoToFile(String path, String content) {
        System.out.println("Linux: Writing to file at " + path + ", content: " + content);
    }

    public String lsFile(String path) {
        return "Linux file info: " + path + ", permissions: rw-r--r--";
    }
}
  • 被适配类,提供了 Linux,Windows 文件相关操作方法。
适配器类
typescript 复制代码
/**
 * Linux 文件系统适配器
 */
public class LinuxFileSystemAdapter implements FileSystem {

    private final LinuxNativeFileOperations linuxFileSystem;

    public LinuxFileSystemAdapter(LinuxNativeFileOperations linuxFileSystem) {
        this.linuxFileSystem = linuxFileSystem;
    }

    @Override
    public void createFile(String path) {
        linuxFileSystem.touchFile(path);
    }

    @Override
    public void deleteFile(String path) {
        linuxFileSystem.rmFile(path);
    }

    @Override
    public String readFile(String path) {
        return linuxFileSystem.catFile(path);
    }

    @Override
    public void writeFile(String path, String content) {
        linuxFileSystem.echoToFile(path, content);
    }

    @Override
    public String getFileInfo(String path) {
        return linuxFileSystem.lsFile(path);
    }
}

/**
 * windows 文件系统 适配器
 */
public class WindowsFileSystemAdapter implements FileSystem {

    private final WindowsNativeFileSystem windowsFileSystem;

    public WindowsFileSystemAdapter(WindowsNativeFileSystem windowsFileSystem) {
        this.windowsFileSystem = windowsFileSystem;
    }

    @Override
    public void createFile(String path) {
        windowsFileSystem.removeFile(path);
    }

    @Override
    public void deleteFile(String path) {
        windowsFileSystem.removeFile(path);
    }

    @Override
    public String readFile(String path) {
        return windowsFileSystem.readFileContent(path);
    }

    @Override
    public void writeFile(String path, String content) {
        windowsFileSystem.writeToFile(path, content);
    }

    @Override
    public String getFileInfo(String path) {
        return windowsFileSystem.getFileMetadata(path);
    }
}
FileManagerFacade
typescript 复制代码
/**
 * 外观类
 */
public class FileManagerFacade {

    /**
     * 文件系统引用(适配器引用)
     */
    private FileSystem currentFileSystem;

    private List<String> operationLog = new ArrayList<>();

    public FileManagerFacade(String osType) {
        switch (osType.toLowerCase()) {
            case "windows":
                currentFileSystem = new WindowsFileSystemAdapter(new WindowsNativeFileSystem());
                break;
            case "linux":
                currentFileSystem = new LinuxFileSystemAdapter(new LinuxNativeFileOperations());
                break;
            default:
                throw new IllegalArgumentException("Unsupported OS type: " + osType);
        }
    }

    public void switchFileSystem(String osType) {
        switch (osType.toLowerCase()) {
            case "windows":
                currentFileSystem = new WindowsFileSystemAdapter(new WindowsNativeFileSystem());
                break;
            case "linux":
                currentFileSystem = new LinuxFileSystemAdapter(new LinuxNativeFileOperations());
                break;
            default:
                throw new IllegalArgumentException("Unsupported OS type: " + osType);
        }

        this.logOperation("Switched to " + osType + " file system");
    }

    /**
     * 创建并写入文件
     *
     * @param path
     * @param content
     */
    public void createAndWriteFile(String path, String content) {
        currentFileSystem.createFile(path);
        currentFileSystem.writeFile(path, content);
        this.logOperation("Created and wrote to file: " + path);
    }

    /**
     * 读取并删除文件
     *
     * @param path
     * @return
     */
    public String readAndDeleteFile(String path) {
        String content = currentFileSystem.readFile(path);
        currentFileSystem.deleteFile(path);
        this.logOperation("Read and deleted file: " + path);
        return content;
    }

    /**
     * 获取文件信息摘要
     *
     * @param path 文件地址
     * @return
     */
    public String getFileSummary(String path) {
        String info = currentFileSystem.getFileInfo(path);
        this.logOperation("Retrieved file info: " + path);
        return "File Summary: \n" + info + "\nContent Preview: " + currentFileSystem.readFile(path).substring(0, Math.min(20, info.length()));
    }

    public List<String> getOperationLog() {
        return new ArrayList<>(operationLog);
    }

    private void logOperation(String message) {
        operationLog.add(message);
    }
}
  • FileManagerFacade 外观类,持有 FileSystem 接口的引用,并在原有接口基础上,整合繁杂接口,提供统一的高层次接口。
测试类
csharp 复制代码
    @Test
    public void test_file() {
        FileManagerFacade fileManager = new FileManagerFacade("windows");

        System.out.println("=== Window 文件操作 ===");
        fileManager.createAndWriteFile("D:\test\demo.txt", "Hello Windows");
        System.out.println(fileManager.getFileSummary("D:\test\demo.txt"));

        fileManager.switchFileSystem("linux");

        System.out.println("\n=== Linux 文件操作 ===");
        fileManager.createAndWriteFile("/home/user/test.txt", "Hello Linux");
        System.out.println(fileManager.readAndDeleteFile("/home/user/test.txt"));

        System.out.println("\n=== 操作日志 ===");
        fileManager.getOperationLog().forEach(System.out::println);

    }
运行结果
vbnet 复制代码
=== Window 文件操作 ===
Windows: Deleting file at D:\test\demo.txt
Windows: Writing to file at D:\test\demo.txt, content: Hello Windows
File Summary: 
Windows file info: D:\test\demo.txt, size: 1024 bytes
Content Preview: Windows file content

=== Linux 文件操作 ===
Linux: Creating file at /home/user/test.txt
Linux: Writing to file at /home/user/test.txt, content: Hello Linux
Linux: Removing file at /home/user/test.txt
Linux file content from /home/user/test.txt

=== 操作日志 ===
Created and wrote to file: D:\test\demo.txt
Retrieved file info: D:\test\demo.txt
Switched to linux file system
Created and wrote to file: /home/user/test.txt
Read and deleted file: /home/user/test.txt

Process finished with exit code 0
优势
  1. 接口统一与简化
  • 适配器模式:统一不同操作系统的文件操作接口
  • 外观模式:提供更高级的简化操作
  1. 降低系统复杂度
  • 适配器隐藏了不同平台的实现细节
  • 外观提供了更简单的接口,屏蔽了复杂操作序列

长话短说

组合后的优势

解耦

适配器模式本身就致力于解耦不兼容的接口,当它与工厂模式、策略模式等组合时,进一步将对象的创建、 行为选择等负责逻辑从核心业务逻辑中分离出来。

开闭原则的遵循

组合模式使得系统能够更好的遵循 "开闭原则",当有新的需求时,通常只需要添加新的类,而无需修改已有 的稳定代码,大大降低了系统维护的成本和引入新错误的风险。

动态行为

装饰器模式的引入,使得我们可以在运行时动态的为对象添加或移除功能,而无需修改其原始类,这种灵活性 在需要根据不同场景或用户偏好提供不同功能组合时尤为重要。

适配器模式负责接口转换, 工厂模式负责对象创建, 策略模式负责行为封装, 装饰器模式负责功能增强, 外观模式负责简化复杂子系统。

目前还没探索出相对合理的学习路线,只能通过不断联系,熟悉各种设计模式,并进行代码练习,以达到熟能生巧的作用。

相关推荐
杨充1 天前
10.可测试性实战设计
设计模式·开源·代码规范
杨充1 天前
9.重构十二式的实战
设计模式·开源·代码规范
杨充1 天前
6.设计原则的全景图
设计模式·开源·全栈
杨充1 天前
2.面向对象的特性
设计模式
杨充1 天前
7.SOLID原则案例汇
设计模式·开源·全栈
杨充1 天前
8.反模式与坏味道
设计模式·开源·代码规范
杨充1 天前
3.接口vs抽象类比较
设计模式
咖啡八杯1 天前
文法、BNF与AST
java·设计模式·解释器模式·ast·文法
咖啡八杯2 天前
GoF设计模式——解释器模式
java·后端·spring·设计模式