前言
目前正在出一个设计模式专题系列教程, 篇幅会较多, 喜欢的话,给个关注❤️ ~
本节给大家讲一下设计模式中的适配器模式,并结合实际业务场景给大家讲解如何使用~
本专题的所有案例代码主要以Java语言为主, 好了, 废话不多说直接开整吧~
适配器模式
适配器模式是一种结构型设计模式,其主要目的是将一个类的接口转换为另一个接口,以满足不同类之间的兼容性。
适配器模式涉及到以下角色:
- 目标接口(
Target):这是客户端代码想要使用的接口,但它与客户端代码原本持有的接口不同。 - 源接口(
Adaptee):这是客户端代码原本持有的接口,但它与目标接口不兼容。 - 适配器(
Adapter):这是一个转换器,将源接口转换为目标接口。
适配器模式可以分为两种类型:类适配器和对象适配器。
- 类适配器:通过继承源接口和目标接口来实现适配器。这种方式要求源接口必须是一个类而不是一个接口,因此不太常用。
- 对象适配器:通过持有一个源接口实例和目标接口实例来实现适配器。这种方式可以适用于源接口是一个接口或类的情况。
下面我们来看一个对象适配器的例子,假设我们有一个第三方库,它提供了一个名为 ThirdPartyLibrary 的类,该类的接口如下:
public class ThirdPartyLibrary {
public void doSomethingElse(int value) {
System.out.println("Doing something else with value: " + value);
}
}
而我们的客户端代码中使用的是一个名为 OurLibraryInterface 的接口,其接口如下:
public interface OurLibraryInterface {
void doSomething(int value);
}
我们现在需要将ThirdPartyLibrary的接口转换为 OurLibraryInterface 的接口,以便在客户端代码中使用。
我们可以通过对象适配器模式来实现这一目标。首先,我们定义一个适配器类 ThirdPartyLibraryAdapter,该类实现了 OurLibraryInterface 接口,并持有一个 ThirdPartyLibrary 实例:
接着,我们在客户端代码中创建一个 ThirdPartyLibrary 的实例,并使用适配器类 ThirdPartyLibraryAdapter 将其转换为 OurLibraryInterface 的接口:
public class AdapterTest {
public static void main(String[] args) {
// 创建第三方库实例
ThirdPartyLibrary library = new ThirdPartyLibrary();
// 创建适配器实例
OurLibraryInterface adapter = new ThirdPartyLibraryAdapter(library);
// 在客户端代码中使用适配器
adapter.doSomething(42);
}
}
在上面的示例中,我们创建了一个 ThirdPartyLibrary 的实例,并通过适配器类 ThirdPartyLibraryAdapter 将其转换为 OurLibraryInterface 的接口。然后我们在客户端代码中使用适配器实例调用了 doSomething 方法,该方法实际上是调用了 ThirdPartyLibrary 的 doSomethingElse 方法。通过适配器模式,我们成功地将第三方库的接口转换为了客户端代码需要的接口,使得客户端代码能够正常工作。
适配器模式在实际开发中经常被使用,特别是在集成不同系统或库时。例如,我们可能需要将一个旧的库或系统接口转换为新的接口,以便与现有的代码进行交互。适配器模式可以帮助我们快速地完成这种转换工作,提高代码的复用性和可维护性。
最佳实践
同样以电商平台为例,假设我们的电商平台需要集成多个不同的第三方物流服务提供商,这些物流服务提供商的接口不一致,我们可以使用适配器模式来统一物流服务接口,使得我们的电商平台可以适配多种物流服务提供商。具体实现代码如下:
首先,我们定义一个 LogisticsService 接口,包含了电商平台需要的物流服务功能:
public interface LogisticsService {
void createOrder(String orderId, String senderAddress, String receiverAddress);
void cancelOrder(String orderId);
void queryOrder(String orderId);
}
然后,我们创建一个适配器接口 LogisticsServiceAdapter,它用于适配不同的物流服务提供商接口:
public interface LogisticsServiceAdapter {
void createOrder(String orderId, String senderAddress, String receiverAddress);
void cancelOrder(String orderId);
void queryOrder(String orderId);
}
接着,我们创建多个不同的物流服务提供商接口实现类:
- 顺丰物流
public class SFExpressService {
public void createShipment(String orderId, String senderAddress, String receiverAddress) {
System.out.println("SF Express created shipment for order " + orderId + " from " + senderAddress + " to " + receiverAddress);
}
public void cancelShipment(String orderId) {
System.out.println("SF Express cancelled shipment for order " + orderId);
}
public void queryShipment(String orderId) {
System.out.println("SF Express queried shipment for order " + orderId);
}
}
- 韵达物流
public class YundaExpressService {
public void createOrder(String orderId, String senderAddress, String receiverAddress) {
System.out.println("Yunda Express created order for " + orderId + " from " + senderAddress + " to " + receiverAddress);
}
public void cancelOrder(String orderId) {
System.out.println("Yunda Express cancelled order for " + orderId);
}
public void getOrderStatus(String orderId) {
System.out.println("Yunda Express queried order status for " + orderId);
}
}
接着,我们创建适配器类 SFExpressServiceAdapter,它封装了顺丰快递的接口,并将其转换为 LogisticsServiceAdapter 接口的实现:
public class SFExpressServiceAdapter implements LogisticsServiceAdapter {
private SFExpressService sfExpressService;
public SFExpressServiceAdapter(SFExpressService sfExpressService) {
this.sfExpressService = sfExpressService;
}
@Override
public void createOrder(String orderId, String senderAddress, String receiverAddress) {
sfExpressService.createShipment(orderId, senderAddress, receiverAddress);
}
@Override
public void cancelOrder(String orderId) {
sfExpressService.cancelShipment(orderId);
}
@Override
public void queryOrder(String orderId) {
sfExpressService.queryShipment(orderId);
}
}
同样的韵达也是这样:
public class YundaExpressServiceAdapter implements LogisticsServiceAdapter {
private YundaExpressService yundaService;
public YundaExpressServiceAdapter(YundaExpressService yundaService) {
this.yundaService = yundaService;
}
@Override
public void createOrder(String orderId, String senderAddress, String receiverAddress) {
yundaService.createOrder(orderId, senderAddress, receiverAddress);
}
@Override
public void cancelOrder(String orderId) {
yundaService.cancelOrder(orderId);
}
@Override
public void queryOrder(String orderId) {
yundaService.getOrderStatus(orderId);
}
}
最后我们实现物流平台的功能服务,对其接口进行封装
public class LogisticsPlatformService implements LogisticsService{
private LogisticsServiceAdapter logisticsServiceAdapter;
public LogisticsPlatformService(LogisticsServiceAdapter logisticsServiceAdapter) {
this.logisticsServiceAdapter = logisticsServiceAdapter;
}
@Override
public void createOrder(String orderId, String senderAddress, String receiverAddress) {
logisticsServiceAdapter.createOrder(orderId, senderAddress, receiverAddress);
}
@Override
public void cancelOrder(String orderId) {
logisticsServiceAdapter.cancelOrder(orderId);
}
@Override
public void queryOrder(String orderId) {
logisticsServiceAdapter.queryOrder(orderId);
}
}
接着,测试一下:
public class LogisticsPlatformServiceTest {
public static void main(String[] args) throws Exception {
SFExpressService sfExpressService = new SFExpressService();
LogisticsServiceAdapter sfAdapter = new SFExpressServiceAdapter(sfExpressService);
LogisticsService service = new LogisticsPlatformService(sfAdapter);
service.cancelOrder("顺丰订单取消");
}
}
输出:
SF Express cancelled shipment for order 顺丰订单取消
需要注意的是,在使用适配器模式时,应该尽量避免出现大量的嵌套适配器。如果需要多次适配,建议对原接口进行重构,尽量减少适配器的使用次数,以提高代码的可维护性和可读性。
结束语
设计模式其实并不难,大家在学习的时候一定要在理解的基础上去写代码,不要去背代码。下节给大家讲桥接模式~
本着把自己知道的都告诉大家,如果本文对您有所帮助,点赞+关注鼓励一下呗~
相关文章
项目源码(源码已更新 欢迎star⭐️)
Kafka 专题学习
- 一起来学kafka之Kafka集群搭建
- 一起来学kafka之整合SpringBoot基本使用
- 一起来学kafka之整合SpringBoot深入使用(一)
- 一起来学kafka之整合SpringBoot深入使用(二)
- 一起来学kafka之整合SpringBoot深入使用(三)
项目源码(源码已更新 欢迎star⭐️)
ElasticSearch 专题学习
项目源码(源码已更新 欢迎star⭐️)
往期并发编程内容推荐
- Java多线程专题之线程与进程概述
- Java多线程专题之线程类和接口入门
- Java多线程专题之进阶学习Thread(含源码分析)
- Java多线程专题之Callable、Future与FutureTask(含源码分析)
- 面试官: 有了解过线程组和线程优先级吗
- 面试官: 说一下线程的生命周期过程
- 面试官: 说一下线程间的通信
- 面试官: 说一下Java的共享内存模型
- 面试官: 有了解过指令重排吗,什么是happens-before
- 面试官: 有了解过volatile关键字吗 说说看
- 面试官: 有了解过Synchronized吗 说说看
- Java多线程专题之Lock锁的使用
- 面试官: 有了解过ReentrantLock的底层实现吗?说说看
- 面试官: 有了解过CAS和原子操作吗?说说看
- Java多线程专题之线程池的基本使用
- 面试官: 有了解过线程池的工作原理吗?说说看
- 面试官: 线程池是如何做到线程复用的?有了解过吗,说说看
- 面试官: 阻塞队列有了解过吗?说说看
- 面试官: 阻塞队列的底层实现有了解过吗? 说说看
- 面试官: 同步容器和并发容器有用过吗? 说说看
- 面试官: CopyOnWrite容器有了解过吗? 说说看
- 面试官: Semaphore在项目中有使用过吗?说说看(源码剖析)
- 面试官: Exchanger在项目中有使用过吗?说说看(源码剖析)
- 面试官: CountDownLatch有了解过吗?说说看(源码剖析)
- 面试官: CyclicBarrier有了解过吗?说说看(源码剖析)
- 面试官: Phaser有了解过吗?说说看
- 面试官: Fork/Join 有了解过吗?说说看(含源码分析)
- 面试官: Stream并行流有了解过吗?说说看
推荐 SpringBoot & SpringCloud (源码已更新 欢迎star⭐️)
博客(阅读体验较佳)
本文正在参加「金石计划」