【设计模式】适配器模式以及源码应用

news/2024/9/27 7:16:06

1  前言

最近看源码的时候,经常看到适配器模式的出现,所以本文来记录一下什么是适配器模式,它的结构特点是什么呢?以及它在源码中的一些应用。

2  适配器模式

2.1  基本概念

适配器模式,适配两个字最能体现其思想,也可以理解为协调、转换,有点类似我们平时见到的各种转换头的作用,它就是一种东西转变为另一种东西的转换器。

适配器模式(Adapter Pattern)是一种常用的设计模式,用于使一个类的接口与另一个类兼容。

适配器模式允许你将已有的类“包装”起来,使其看起来像是另一个接口的一部分,从而让现有类能够与不兼容的接口一起工作。

适配器器模式要解决的主要问题就是多种差异化类型的接⼝做统⼀输出,解决类接口不兼容的问题。

适配器模式提高了系统的灵活性和可扩展性,尤其是在需要复用现有类或实现不同系统间交互时非常有用。

2.2  结构特点

适配器模式涉及以下几个角色:

目标:这是期望的接口,适配器将被设计成实现这个接口。

被适配者:这是已有类的接口,它提供了一些有用的功能,但接口与目标不兼容。

适配器:这是一个新的类,它持有被适配者的实例,并且实现目标接口。适配器将被适配者的接口转换成目标接口所期望的形式。

2.3  适用场景

类接口不兼容:当你有一个类,它的接口不符合现有的系统要求时,可以使用适配器模式来调整这个类的接口,使其适应现有系统的需要。

复用现有类:当你想复用一些有用的类,但这些类的接口不符合你的需求时,可以通过适配器模式来改变接口,使其符合你的需求。

系统间的交互:当两个系统之间需要交互,但它们的接口不同时,可以使用适配器模式来实现中间层,使得这两个系统可以相互通信。

3  源码应用

3.1  JDK 中的 RunnableFuture、RunnableAdapter

我们平时用到的线程池 ThreadPoolExecutor,我们提交异步任务的时候,可以提交 Runnable 或者 Callable 的任务。

public Future<?> submit(Runnable task) {...}
public <T> Future<T> submit(Callable<T> task) {...}

但我们都知道线程池最后的落点就是线程,所以异步任务最后都是要给到 Thread 去执行的,而 Thread 我们会发现不管是它的构造器还是属性都只接纳 Runnable 类型的。

private Runnable target;
public Thread(Runnable target) {...}
public Thread(Runnable target, String name) {...}
Thread(Runnable target, AccessControlContext acc) {...}

所以这里就需要用到适配器来去兼容,那就需要一个适配器类能实现 Runnable 接口并能接收 Callable 参数,统一 Runnable、Callable 两者的行为,它就是 RunnableFuture(这是一个接口,它的具体实现类是 FutureTask)。

我们看线程池提交任务:

// 提交 Runnable 的
public Future<?> submit(Runnable task) {if (task == null) throw new NullPointerException();RunnableFuture<Void> ftask = newTaskFor(task, null);execute(ftask);return ftask;
}
// 提交 Callable 的
public <T> Future<T> submit(Callable<T> task) {if (task == null) throw new NullPointerException();RunnableFuture<T> ftask = newTaskFor(task);execute(ftask);return ftask;
}

可以看到提交后,都会用 newTaskFor 把 Runnable 和 Callable 都包装成了 RunnableFuture 的实现类 FutureTask。

public interface RunnableFuture<V> extends Runnable, Future<V> {...}
public class FutureTask<V> implements RunnableFuture<V> {...}

可以看到 RunnableFuture 继承了 Runnable 所以它的实现类可以作为参数放到 Thread 中去执行。

RunnableFuture 继承了 Future,所以可以通过 get 方法获取到异步任务执行的结果。

它的实现类 FutureTask 在接收到 Callable 或者 Runnable 内部都转换为了 Callable,所以内部还用 RunnableAdapter 对 Runnable 又做了一层适配。

// FutureTask 接收 Runnable
private Callable<V> callable;
public FutureTask(Runnable runnable, V result) {// 将 Runnable 转换为 Callablethis.callable = Executors.callable(runnable, result);this.state = NEW;
}
// Executors.callable 方法转换
public static <T> Callable<T> callable(Runnable task, T result) {if (task == null)throw new NullPointerException();// 适配return new RunnableAdapter<T>(task, result);
}
// RunnableAdapter 实现 Callable,并能接收 Runnable 的参数,实际执行的时候,调用内部的 Runnable
static final class RunnableAdapter<T> implements Callable<T> {final Runnable task;final T result;RunnableAdapter(Runnable task, T result) {this.task = task;this.result = result;}public T call() {task.run();return result;}
}
// FutureTask 接收 Callable
public FutureTask(Callable<V> callable) {if (callable == null)throw new NullPointerException();this.callable = callable;this.state = NEW;
}

可以看到线程池这里涉及到两层适配

RunnableFuture 本身实现了 Runnable ,将 Callable 类型的任务可以交给线程去执行,完成 Callable -> Runnable 的适配。

RunnableAdapter 由于 FutureTask 内部统一逻辑为处理 Callable 类型的,所以当接收到 Runnable 类型的,通过 RunnableAdapter 完成 Runnable -> Callable 的适配,统一内部逻辑。

3.2  SpringValidatorAdapter

前面 我们看的【SpringBoot】@Validated @Valid 参数校验概述以及使用方式、【SpringBoot】@Validated @Valid 注解校验时机实现原理,关于参数校验的两类体系,一个是 javax 包里的 @Valid 一个是 Spring 本身的 @Validated。

@Valid 本身的体系已经能完成很多约束的校验,而在 Spring 中想把它融合进来,所以 SpringValidatorAdapter 就出现了。

可以看到 SpringValidatorAdapter 实现了 Validator(Spring自己的) 和 Validator(javax包里的),并且持有 javax.validation.Validator 的属性。

public interface SmartValidator extends Validator {...}
public class SpringValidatorAdapter implements SmartValidator, javax.validation.Validator {private javax.validation.Validator targetValidator;public SpringValidatorAdapter(javax.validation.Validator targetValidator) {Assert.notNull(targetValidator, "Target Validator must not be null");this.targetValidator = targetValidator;}void setTargetValidator(javax.validation.Validator targetValidator) {this.targetValidator = targetValidator;}...
}

SpringValidatorAdapter 的子类也是 Spring 默认的校验器 LocalValidatorFactoryBean,在 InitializingBean 执行 afterPropertiesSet 的时候,通过构造 Configuration 继而得到 ValidatorFactory 继而通过工厂得到 Validator(javax包里的),然后通过 setTargetValidator 方法给属性赋值。

public class LocalValidatorFactoryBean extends SpringValidatorAdapterimplements ValidatorFactory, ApplicationContextAware, InitializingBean, DisposableBean {public void afterPropertiesSet() {Configuration<?> configuration;...// Allow for custom post-processing before we actually build the ValidatorFactory.
        postProcessConfiguration(configuration);this.validatorFactory = configuration.buildValidatorFactory();setTargetValidator(this.validatorFactory.getValidator());}
}

继而在参数校验的时候,可以使用到 javax.validation.Validator 的功能。

// private javax.validation.Validator targetValidator;
@Override
public void validate(Object target, Errors errors, Object... validationHints) {if (this.targetValidator != null) {processConstraintViolations(this.targetValidator.validate(target, asValidationGroups(validationHints)), errors);}
}

所以 SpringValidatorAdapter 完成了 javax 包里的 Validator 到 Spring 中的适配融合。

4  小结

好啦,关于适配器模式的就介绍到这里,其实方向有点反了,应该先准备好设计模式的,然后边看源码的过程中发现有用到,再贴里边,所以之前看过的涉及到设计模式有点忘了都= =,后续回忆起来别的地方我再补进来,有理解不对的地方还请指正哈。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/65257.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

Windows10永久拒绝升级Win11

一、使用组策略阻止升级到windows11 需要专业版或企业版的Windows 10才能访问组策略编辑器。以下是操作步骤:单击开始菜单,输入gpedit.msc,打开本地组策略编辑器。 导航到“计算机配置”>“管理模板”>“Windows组件”>“Windows更新”>“适用于企业的Windows更…

arcgis怎样把面图层按另一面图层分割

摘自https://jingyan.baidu.com/article/6079ad0e9b5c8428fe86db70.htmlarcgis的桌面软件 主要应用于空间数据处理和管理,工作中往往会遇到要批量分割大量的面状数据,并且要按照其所处面的关系赋值。1、打开ArcMap软件,把两个面图层都加载到视图区域内,如下图2、在工具栏中…

10 #### 继承

继承 一定时刻知道,self是哪个类的对象。只要知道是哪个类的对象,就从哪个类开始进行查找 class base: # 父类,也可称为基类pass class Info(base): # 子类,也可称为派生类passclass base: # 父类,也可称为基类def f1(self):print(f1)class Info(base): # 子类,也可称为…

03 对象到底是个啥?

对象到底是个啥? # 2. 基于面向对象的方式实现上述功能 # 定义类 class MessageInfo:# 方法def send_email(to, body):msg = f给{to}发送邮件,内容:{body}print(msg)# 方法def send_dingding(to, body):msg = f给{to}发送邮件,内容:{body}print(msg)# 方法def send_wechat…

04 特殊方法`__init__(self)`

特殊方法__init__(self) class MessageInfo:# 初始化方法def __init__(self, city):self.city = cityself.company = 联通# 方法def send_email(self, to, body):msg = f给{to}发送邮件,内容:{body}print(msg)# 方法def send_dingding(self, to, body):msg = f给{to}发送邮件…

2025 年度技术规划

• quant 无代码图书配代码 • 小说大纲批量提取 • gh高星代码注释和漏洞检测 • 反编译 1000+ apk • 至少一个游戏辅助 • 漫画翻译 • 玄学pdf转md • dao改造 • pmp • kaggle master

文件上传

1、准备工作对于文件上传,浏览器在上传的过程中是将文件以流的形式提交到服务器端的。 一般采用Apache的开源工具common-fileupload这个文件上传组件。 common-fileupload是依赖于common-io这个包的,所以还需要下载这个包。我们下载最新的jar包:common-fileupload :https://m…

java web

1、基本概念 1.1、web开发: web开发:web,网页的意思,www.baidu.com 静态webhtml,css 提供给所有人看的数据始终不会发生变化!动态web淘宝,几乎是所有的网站 提供给所有的人看的数据始终会变化,每个人在不同的时间,不同的地点看到不同的信息 技术栈:Servlet/Jsp,ASP,…