书接上回,本篇讲一下结构型模式-适配器设计模式
适配器设计模式
定义:将一个类的接口转换成客户期望的另外一个接口。它能使得原本由于接口不兼容而不能一起工作的那些类可以一起工作了。
代码表示格式:
对象适配器---组合方式
Target:现有的接口,可以理解现有项目能直接使用的接口规范
Adaptee:被适配的类,可以理解为一套外来逻辑,不符合现有的操作规范,项目无法操作
Adapter:适配器,实现Target接口,满足项目可操作。再通过组合方式操作Adaptee。
现有项目--操作---Target---操作--Adapter--操作-Adaptee
Target
//现有接口
//项目约定规范-可操作
public interface Target {
void request();
}
Adaptee
//外来逻辑-被适配
//不符合现有的操作规范,项目无法操作
public class Adaptee {
//外来逻辑
public void specificRequest(){
System.out.println("外来(被适配)逻辑执行");
}
}
Adapter
//适配器
//建立系统与被适配者的链接(建立操作规范)
//现有项目--操作---Target---操作--Adapter--操作-Adaptee
public class Adapter implements Target{
//被适配对象
private Adaptee adaptee; //组合方式
public Adapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
//适配规范
@Override
public void request() {
System.out.println("通过适配器完成适配....");
adaptee.specificRequest();
}
}
测试
public class App {
public static void main(String[] args) {
//现有项目(App)--操作---Target---操作--Adapter--操作-Adaptee
//外来逻辑
Adaptee adaptee = new Adaptee();
//现有接口
Target target = new Adapter(adaptee);
//现有项目(App)--操作---Target---操作(request)--Adapter--操作-Adaptee(specificRequest)
target.request();
}
}
结果
通过适配器完成适配....
外来(被适配)逻辑执行
类适配器---继承方式
这种方式跟上面方式大同小异,唯一的区别是Adapter编写,对象适配器使用时组合方式,类适配器使用使继承。
其他类不变,只有修改Adapter类即可
//适配器
//建立系统与被适配者的链接(建立操作规范)
//现有项目--操作---Target---操作--Adapter--操作-Adaptee
public class Adapter extends Adaptee implements Target {
//被适配对象
@Override
public void request() {
System.out.println("通过适配器完成适配....");
super.specificRequest();
}
}
从上面代码上看,适配器模式核心逻辑就是接口转换:
现有接口------适配器------外来逻辑
也可以说:
现有规范------适配器------外来规范
案例分析
需求:模拟Type-C线转35mm耳机线转接头
背景:华为Note10 使用type-C孔的耳机,手里只有35mm耳机
思路分析:
转接头-----Adapter
手机Type-C口----Target
35mm耳机---Adaptee
ITypeC
//Type-C接口
public interface ITypeC {
void listen();
}
Headset35MM
//33mm耳机线
public class Headset35MM {
public void listen33MM(){
System.out.println("33mm耳机听歌....");
}
}
TypeCTo33MM
//type-c 转 33mm线
public class TypeCTo33MM implements Target {
//33mm耳机线
private Headset35MM headset35MM;
public TypeCTo33MM(Headset35MM headset35MM) {
this.headset35MM = headset35MM;
}
@Override
public void request() {
System.out.println("转接头一端连接type-c,一端连接33mm线....");
headset35MM.listen33MM();
}
}
Phone
//手机
public class Phone {
public static void main(String[] args) {
//33mm耳机线
Headset35MM mm = new Headset35MM();
//转换头
TypeCTo33MM t2m = new TypeCTo33MM(mm);
//接驳,听歌
t2m.request();
}
}
结果
转接头一端连接type-c,一端连接33mm线....
33mm耳机听歌....
解析
一切在代码中,没啥要解析了。
适用场景
1>已经存在的类,他的方法和需求不匹配时可用
2>不是软件设计阶段考虑的设计模式,是随着软件维护,由于不同产品,不同厂家造成功能类似而接口不相同情况下可以使用。 (维护旧系统时用得相对多)
优缺点
优点
能提高类的透明性和复用性,只要接口不兼容都可以使用
目标类和适配器类解耦,可以根据需要与实际自个写规范,从而提高程序扩展性
符合开闭原则
缺点
适配器编写过程需要全面考虑,过度使用会让系统很凌乱,也会增加系统的复杂性,也会提高系统代码可读成本
VS装饰器模式
适配器模式跟装饰模式看起有点类似,因为都使用了组合方式实现,但是也仅仅是类似。它们的目的与本质都不一样。
适配器模式:强调是转换(A->B)
装饰器模式:强调是功能叠加
VS外观模式
适配器模式跟外观模式看起也有点类似,原因也是组合方式实现,但是也仅仅是类似。它们的目的与本质都不一样。
适配器模式:强调是转换(A->B)
外观模式:强调封装交互,隐藏内部(子系统)实现细节
vs代理模式
适配器模式跟代理模式很像很像,代码实现上基本一样,那却别在哪?却别在对外来逻辑的侧重点上。
适配器模式:
1>目的是消除外来逻辑给系统带来的不兼容问题,建立适配后,以统一规范访问外来逻辑。
2>通过适配类改变访问外来逻辑的方式
代理模式:
1>目的是隔离对复杂对象的访问
2>通过代理类间接访问外来逻辑,但不会改变原有的访问方式
开发案例
本篇适配器模式案例是spring的-HandlerAdapter
HandlerAdapter ---对应---Target
public interface HandlerAdapter {
boolean supports(Object handler);
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
long getLastModified(HttpServletRequest request, Object handler);
}
RequestMappingHandlerAdapter---对应--Adapter
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
implements BeanFactoryAware, InitializingBean {
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
mav = invokeHandlerMethod(request, response, handlerMethod);
return mav;
}
}
所有忒有@RequestMapping相关请求映射方法---对应--Adaptee
比如:
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello(){
return "ok";
}
}
SpringMVC要统一Controller中的映射方法执行规则,使用了HandlerMethod封装,具体实现规则就是adapter类中handleInternal 方法。
总结
适配器模式本质:转换匹配,复用功能,消除访问的不兼容。