妙解设计模式之适配器模式

目录

适配器模式的概念

适配器模式是一种结构设计模式,它允许将接口不兼容的类 通过一个适配器类进行适配,使得这些类可以一起工作。适配器模式通常用于以下情况:

  1. 当一个接口的实现类已经存在,但是另一个接口需要的是不兼容的时候。
  2. 当需要将一个类的功能与另一个类一起使用,但是两者之间的接口不一致时。
  3. 当需要使用一个已经存在的类,但是它的接口不符合当前的需求。

适配器模式包含以下几个组成成员

  1. 目标接口(Target):定义客户端需要的接口,适配器模式的目标对象是实现了目标接口的类。

  2. 适配器(Adapter):实现了目标接口并包含了被适配对象的引用,通过适配器可以将不兼容的类转换为符合目标接口要求的类。

  3. 被适配者(Adaptee):需要适配的类,它包含了原本不符合目标接口的方法。

适配器模式有两种实现方式:类适配器模式和对象适配器模式

  1. 类适配器模式 :适配器类继承自被适配者类,并同时实现了目标接口,通过多重继承将两者结合在一起。

  2. 对象适配器模式 :适配器类持有被适配者类的实例,并通过委托调用被适配者类的方法来实现目标接口。

适配器模式可以帮助我们在系统中快速整合现有的代码,同时也提高代码的可重用性和解耦性。其核心思想是提供一个中间层,充当翻译器的角色,将不兼容的类转换为可兼容的类。

生活中的例子

想象一下,你有一个老旧的玩具车,它只能用旧式的电池(比如圆柱形的AA电池)。现在你买了一些新型的电池(比如方形的电池),这两个电池形状不一样,不能直接用在玩具车上。这时,你需要一个小工具,让新型电池可以用在老旧的玩具车上,这个小工具就是适配器。

在编程中的例子

在编程中,适配器模式帮助我们把一个类的接口转换成另外一个类可以理解的接口。

假设我们有一个旧的接口叫做OldCharger,它有一个方法chargeWithOldCable(),用来给设备充电。但我们现在有一个新设备,这个设备用的是NewCharger接口,方法是chargeWithNewCable()

java 复制代码
// 旧的充电器接口
class OldCharger {
    public void chargeWithOldCable() {
        System.out.println("Charging with old cable");
    }
}

// 新的充电器接口
interface NewCharger {
    void chargeWithNewCable();
}

// 适配器类
class ChargerAdapter implements NewCharger {
    private OldCharger oldCharger;

    public ChargerAdapter(OldCharger oldCharger) {
        this.oldCharger = oldCharger;
    }

    @Override
    public void chargeWithNewCable() {
        oldCharger.chargeWithOldCable();
    }
}

在这个例子中:

  • OldCharger是旧接口。
  • NewCharger是新接口。
  • ChargerAdapter是适配器,它让OldCharger能用NewCharger的接口。

使用适配器的好处

  • 兼容性:你可以在不修改旧代码的情况下使用新功能。
  • 灵活性:可以很容易地切换不同的接口。

软件工程中的实际应用

兼容旧接口

在大型项目中,旧的系统或接口可能会存在很长时间,而新的功能开发需要与这些旧接口兼容。通过适配器模式,可以在不修改旧代码的情况下使新代码与旧接口兼容。

示例 :假设我们有一个旧的支付系统OldPaymentService,它提供的方法是processOldPayment(String details)。新的支付系统NewPaymentService使用processNewPayment(PaymentInfo paymentInfo)

java 复制代码
// 旧的支付系统
class OldPaymentService {
    public void processOldPayment(String details) {
        // 处理旧支付
    }
}

// 新的支付信息类
class PaymentInfo {
    private String cardNumber;
    private String expiryDate;
    private double amount;
    // getters and setters
}

// 新的支付系统接口
interface NewPaymentService {
    void processNewPayment(PaymentInfo paymentInfo);
}

// 适配器类
class PaymentAdapter implements NewPaymentService {
    private OldPaymentService oldPaymentService;

    public PaymentAdapter(OldPaymentService oldPaymentService) {
        this.oldPaymentService = oldPaymentService;
    }

    @Override
    public void processNewPayment(PaymentInfo paymentInfo) {
        String details = paymentInfo.getCardNumber() + ":" + paymentInfo.getExpiryDate() + ":" + paymentInfo.getAmount();
        oldPaymentService.processOldPayment(details);
    }
}

整合第三方库

在项目中,我们可能需要整合不同的第三方库,这些库的接口不统一。通过适配器模式,可以将第三方库的接口统一起来,方便项目中使用。

示例 :假设我们有两个不同的日志库LibraryALoggerLibraryBLogger,我们希望统一使用一个Logger接口。

java 复制代码
// 第一个日志库
class LibraryALogger {
    public void logMessage(String msg) {
        System.out.println("LibraryA: " + msg);
    }
}

// 第二个日志库
class LibraryBLogger {
    public void writeLog(String msg) {
        System.out.println("LibraryB: " + msg);
    }
}

// 日志接口
interface Logger {
    void log(String message);
}

// 第一个日志库的适配器
class LibraryALoggerAdapter implements Logger {
    private LibraryALogger logger;

    public LibraryALoggerAdapter(LibraryALogger logger) {
        this.logger = logger;
    }

    @Override
    public void log(String message) {
        logger.logMessage(message);
    }
}

// 第二个日志库的适配器
class LibraryBLoggerAdapter implements Logger {
    private LibraryBLogger logger;

    public LibraryBLoggerAdapter(LibraryBLogger logger) {
        this.logger = logger;
    }

    @Override
    public void log(String message) {
        logger.writeLog(message);
    }
}

简化复杂接口

在某些情况下,我们可能需要简化一个复杂接口,以便在项目中更容易使用。适配器模式可以提供一个简化的接口来包装复杂的类。

示例 :假设我们有一个复杂的图形库ComplexGraphicsLibrary,它有许多复杂的方法。我们可以使用适配器模式简化它的接口。

java 复制代码
// 复杂的图形库
class ComplexGraphicsLibrary {
    public void drawLine(int x1, int y1, int x2, int y2) {
        // 画线逻辑
    }

    public void drawCircle(int x, int y, int radius) {
        // 画圆逻辑
    }
}

// 简化的图形接口
interface SimpleGraphics {
    void drawShape(String shapeType, int... params);
}

// 适配器类
class SimpleGraphicsAdapter implements SimpleGraphics {
    private ComplexGraphicsLibrary graphicsLibrary;

    public SimpleGraphicsAdapter(ComplexGraphicsLibrary graphicsLibrary) {
        this.graphicsLibrary = graphicsLibrary;
    }

    @Override
    public void drawShape(String shapeType, int... params) {
        if (shapeType.equalsIgnoreCase("line")) {
            graphicsLibrary.drawLine(params[0], params[1], params[2], params[3]);
        } else if (shapeType.equalsIgnoreCase("circle")) {
            graphicsLibrary.drawCircle(params[0], params[1], params[2]);
        }
    }
}

跨平台支持

java 复制代码
// 桌面平台存储接口
class DesktopStorage {
    public void saveFile(String path, String data) {
        // 保存文件到桌面平台
    }
}

// 移动平台存储接口
class MobileStorage {
    public void saveToPath(String path, String data) {
        // 保存文件到移动平台
    }
}

// 统一的存储接口
interface Storage {
    void save(String path, String data);
}

// 桌面平台存储的适配器
class DesktopStorageAdapter implements Storage {
    private DesktopStorage storage;

    public DesktopStorageAdapter(DesktopStorage storage) {
        this.storage = storage;
    }

    @Override
    public void save(String path, String data) {
        storage.saveFile(path, data);
    }
}

// 移动平台存储的适配器
class MobileStorageAdapter implements Storage {
    private MobileStorage storage;

    public MobileStorageAdapter(MobileStorage storage) {
        this.storage = storage;
    }

    @Override
    public void save(String path, String data) {
        storage.saveToPath(path, data);
    }
}

总结

适配器模式是一种结构型设计模式,用于将一个类的接口转换成客户端期望的另一个接口,使得接口不兼容的类可以一起工作。它的主要应用场景包括兼容旧接口、整合第三方库、简化复杂接口和跨平台支持。适配器模式可以提高代码的可维护性和扩展性,减少不同系统之间的耦合。

相关推荐
skyshandianxia11 分钟前
java面试八股之MySQL怎么优化查询语句
java·mysql·面试
kussmcx18 分钟前
开始尝试从0写一个项目--后端(一)
java·spring·maven
yogima27 分钟前
在Spring Data JPA中使用@Query注解
java·数据库·spring
赫萝的红苹果32 分钟前
基于Redisson实现分布式锁
java·spring boot·分布式
wang_book40 分钟前
redis学习(003 数据结构和通用命令)
java·数据库·redis·学习
英雄汉孑43 分钟前
图片压缩代码和实际操作页面
java
薛·1 小时前
记一次因ThreadPoolExecutor多线程导致服务器内存压满问题
java·服务器
胡歌_北京分歌1 小时前
【CentOS 7 上安装 Oracle JDK 8u333】
java·centos
结衣结衣.1 小时前
完全理解C语言函数
java·linux·c语言·数据库·经验分享·笔记
skyshandianxia1 小时前
Java面试八股之MySQL存储货币数据,用什么类型合适
mysql·面试·职场和发展