基本介绍
- 适配器模式(Adaptor Pattern) 将两个本来不兼容的类组合到一起,重新编排转换成一个新的接口给客户端调用,客户端无需知道内部实现的细节,将本不能协同工作类联动起来。
- 适配器属于结构型模式
- 通过继承原类src,实现目标接口dest,完成从src->dest的适配
- 主要可以分为类适配器,对象适配器,接口适配器
角色分类
被适配对象 Src: 原有的对象
目标接口 Dest: 需要输出的目标对象
适配器 Adaptor: 适配器对象 继承原对象获得原对象的属性方法实现目标接口将将原有对象的方法重新编排包装成目标接口的格式输出
例如生活中的案例,充电器的例子,中国交流电压220V无法直接使用到手机上充电,这时就需要一个中间的适配器来讲220V的电压转换成小幅的电压给手机充电,其中的电源适配器的功能就类似我们适配器模式想完成的工作。
代码演示
- 类适配器
// 标准220V电压 被适配对象 src
public class Voltage220V {
int output220V() {
System.out.println("标准220V电压");
return 220;
}
}
// 需要输出的目标接口
public interface Voltage5V {
int output5V();
}
// 继承被适配对象 实现目标接口
public class VoltageAdaptor extends Voltage220V implements Voltage5V {
public int output5V() {
int standard = super.output220V();
System.out.println("返回从220V转换到5V的电压");
return standard - 215;
}
}
// 真正使用到的类
public class Phone {
void charge(Voltage5V voltage5V) {
if (voltage5V.output5V() == 5) {
System.out.println("电压符合正在充电");
} else {
System.out.println("电压不符不能充电");
}
}
}
//测试类
public class AdaptorTest {
public static void main(String[] args) {
Phone phone = new Phone();
VoltageAdaptor voltageAdapter = new VoltageAdaptor();
phone.charge(voltageAdapter);
}
}
类适配器注意事项
- 因为java单继承的特性,适配器只能继承一个原类,而且原类中的其他不需要适配的方法将暴露给子类,子类可以直接覆写父类的方法,增强了灵活性,但是破坏封装违背OCP原则
- 要求目标dest必须是接口
对象适配器
跟类适配器差不多,只是把继承改成了持有目标对象属性的方法,如何合成复用原则,用聚合关系来取代继承关系,下面上代码
// 取消聚合改为成员变量
public class VoltageAdaptor2 implements Voltage5V {
private Voltage220V voltage220V; // 聚合关系
public VoltageAdaptor2(Voltage220V voltage220V) {
this.voltage220V = voltage220V;
}
public int output5V() {
int standard = voltage220V.output220V();
System.out.println("返回从220V转换到5V的电压");
return standard - 215;
}
}
public class AdaptorTest {
public static void main(String[] args) {
Phone phone = new Phone();
// 对象适配器调用
VoltageAdaptor2 voltageAdaptor2 = new VoltageAdaptor2(new Voltage220V());
phone.charge(voltageAdaptor2);
}
}
接口适配器 Default Adapter Pattern
我们在以后使用的过程中发现了由于Adapter实现了目标的接口,这意味着要实现接口的所有方法,如果有4个方法,其中只有1个方法需要适配但是我们暴露了4个方法给调用者不够优雅,改进的方式也十分简单,在接口和Adapter 中添加一个中间层 Default Adapter 其实Default Adapter 什么也没用直接实现了目标接口所有接口空实现,用于调用的Adapter 只需要继承Default Adapter然后复写需要使用的方法即可。
public interface Interface4 {
public void m1();
public void m2();
public void m3();
public void m4();
}
public abstract class AbsAdapter implements Interface4 {
//默认空实现
public void m1() {
}
public void m2() {
}
public void m3() {
}
public void m4() {
}
}
Spring MVC 适配器的应用
org.springframework.web.servlet.config.annotation.WebMvcConfigurer接口
该接口是Spring MVC关于 WebMVC配置的通用接口 相当于了适配器模式目标接口只是这里Spring MVC使用了default 关键字 已经给所有接口方法定义了空实现无需在写一个默认的Default Adapter
public interface WebMvcConfigurer {
default void configurePathMatch(PathMatchConfigurer configurer) {
}
default void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
}
// ....
}
org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration实现
这个接口的addArgumentResolvers方法重写了父类接口的方法相当于适配器
class WebMvcSecurityConfiguration implements WebMvcConfigurer, ApplicationContextAware {
private BeanResolver beanResolver;
@Override
@SuppressWarnings("deprecation")
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
// 方法省略
}
@Bean
public RequestDataValueProcessor requestDataValueProcessor() {
return new CsrfRequestDataValueProcessor();
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.beanResolver = new BeanFactoryResolver(applicationContext.getAutowireCapableBeanFactory());
}
}
又如Spring MVC DispatcherServlet中doDispatch方法
// 省略上下文代码
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
// 遍历集合中所有的handlerAdapters
for (HandlerAdapter adapter : this.handlerAdapters) {
// 是否支持这种handler
if (adapter.supports(handler)) {
return adapter;
}
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
这样匹配了这种能处理不同类型请求的Adapter使得整个架构更加容易扩展