目录
1.定义
通过增加一个新的适配器类来解决接口不兼容的问题,使得原本没有任何关系的类可以协同工作
原来的接口无法直接使用,通过适配器实现对应的功能
2.分类
具体又可以划分为类适配器、对象适配器
3.角色
- 目标抽象类:Target,该角色把其他类转换为我们期望的接口,可以是一个抽象类或接口,也可以是具体类。
- 被适配者: Adaptee ,原有的接口,也是希望被适配的接口。
- 适配器: Adapter, 将被适配者和目标抽象类组合到一起的类。
4.类适配器
被适配类:Adaptee
public class Adaptee {
public void adapterRequest(){
System.out.println("被适配者的方法");
}
}
目标抽象:Target接口
public interface Target {
void request();
}
怎么才可以在目标接口中的 request() 调用 Adaptee 的 adapteeRequest() 方法呢?
public class Adapter extends Adaptee implements Target {
@Override
public void request() {
super.adapterRequest();
}
}
测试:
public class Test {
public static void main(String[] args) {
Target adapterTarget = new Adapter();
adapterTarget.request();
}
}
类适配器的大致结构图如下所示,目标接口定义规范,适配器类相当于多了一层包装,可以调用到被适配者的方法。但是具体去实现一个目标接口的实现类是不能接触到被适配者类的属性和方法的。这样我们即可在新接口 Target 中适配旧的接口或类,然后供别人使用。
也就是说:我打个比方,有的人喜欢看电子档书籍,有的人喜欢看纸质档。但是已有的就只是电子档,所以我们要增加一个步骤(在这里就是增加一个新接口),把电子档的书籍打印成纸质书籍给别人使用。
5.对象适配器
public class Adaptee {
public void adapterRequest(){
System.out.println("被适配者的方法");
}
}
public interface Target {
void request();
}
public class ConcreteTarget implements Target {
public void request() {
System.out.println("concreteTarget目标方法");
}
}
前面的这些类和类适配器中的一样。
对象适配器与类适配器不同之处在于,类适配器通过继承来完成适配,对象适配器则是通过关联来完成,这里稍微修改一下 Adapter 类即可将转变为对象适配器,把被适配者Adaptee关联进入适配器类Adapter
public class Adapter implements Target {
private Adaptee adaptee = new Adaptee();
public void request() {
adaptee.adapterRequest();
}
}
结果一样,只是Adapter类与Adaptee类的关系不同而已(一个是继承,一个是组合)。(Adapter类与Adaptee类在这里是组合关系,两个生命周期一致)
6.应用场景
spring MVC中的适配器模式
Spring MVC中的适配器模式主要用于执行目标 Controller 中的请求处理方法。
在Spring MVC中,DispatcherServlet 作为用户,HandlerAdapter 作为期望接口,具体的适配器实现类用于对目标类进行适配,Controller 作为需要适配的类。
为什么要在 Spring MVC 中使用适配器模式?Spring MVC 中的 Controller 种类众多,不同类型的 Controller 通过不同的方法来对请求进行处理。如果不利用适配器模式的话,DispatcherServlet 直接获取对应类型的 Controller,需要的自行来判断,像下面这段代码一样:
if(mappedHandler.getHandler() instanceof MultiActionController){
((MultiActionController)mappedHandler.getHandler()).xxx
}else if(mappedHandler.getHandler() instanceof XXX){
...
}else if(...){
...
}
这样假设如果我们增加一个 HardController,就要在代码中加入一行 if(mappedHandler.getHandler() instanceof HardController),这种形式就使得程序难以维护,也违反了设计模式设计模式中的开闭原则 – 对扩展开放,对修改关闭。
我们来看看源码,首先是适配器接口 HandlerAdapter
public interface HandlerAdapter {
//判断是不是该实现类适配器
boolean supports(Object var1);
// 处理请求
ModelAndView handle(HttpServletRequest var1, HttpServletResponse var2, Object var3) throws Exception;
long getLastModified(HttpServletRequest var1, Object var2);
}
现该接口的适配器每一个 Controller 都有一个适配器与之对应,这样的话,每自定义一个 Controller 需要定义一个实现 HandlerAdapter 的适配器。
springmvc 中提供的 Controller 实现类有如下
springmvc 中提供的 HandlerAdapter 实现类如下
举例:HttpRequestHandlerAdapter 这个适配器代码如下
public class HttpRequestHandlerAdapter implements HandlerAdapter {
public HttpRequestHandlerAdapter() {
}
public boolean supports(Object handler) {
return handler instanceof HttpRequestHandler;
}
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
((HttpRequestHandler)handler).handleRequest(request, response);
return null;
}
public long getLastModified(HttpServletRequest request, Object handler) {
return handler instanceof LastModified ? ((LastModified)handler).getLastModified(request) : -1L;
}
}
过程:
当Spring容器启动后,会将所有定义好的适配器对象存放在一个List集合中,当一个请求来临时,DispatcherServlet 会通过 handler 的类型找到对应适配器,并将该适配器对象返回给用户,然后就可以统一通过适配器的 hanle() 方法来调用 Controller 中的用于处理请求的方法。
public class DispatcherServlet extends FrameworkServlet {
private List<HandlerAdapter> handlerAdapters;
//初始化handlerAdapters
private void initHandlerAdapters(ApplicationContext context) {
//..省略...
}
// 遍历所有的 HandlerAdapters,通过 supports 判断找到匹配的适配器
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
for (HandlerAdapter ha : this.handlerAdapters) {
if (logger.isTraceEnabled()) {
logger.trace("Testing handler adapter [" + ha + "]");
}
if (ha.supports(handler)) {
return ha;
}
}
}
// 分发请求,请求需要找到匹配的适配器来处理
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
// 确定当前请求的匹配的适配器.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
ha.getLastModified(request, mappedHandler.getHandler());
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
}
// ...省略...
}
优点:
通过适配器模式我们将所有的 controller 统一交给 HandlerAdapter 处理,免去了写大量的 if-else 语句对 Controller 进行判断,也更利于扩展新的 Controller 类型
7.总结
优点:
- 将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,无须修改原有结构。
- 增加了类的透明性和复用性,将具体的业务实现过程封装在适配者类中,对于客户端类而言是透明的,而且提高了适配者类的复用性,同一个适配者类可以在多个不同的系统中复用。
- 灵活性和扩展性都非常好,通过使用配置文件,可以很方便地更换适配器,也可以在不修改原有代码的基础上增加新的适配器类,符合“开闭原则”
缺点:
- 会让系统看起来很复杂,不易维护(因为需要从全局考虑)
- 增加代码的阅读难度
适配器模式最好在详细设计不要考虑它,它不是为了解决还处在开发阶段的问题,而是解决正在服役的项目问题。
8.转载
设计模式之适配器模式_codingXT的博客-CSDN博客_适配器模式
9.其他版本适配器
这个版本说的是,不同参数进去,得到相同的结果
不同消息类型:
@Data
@Builder
public class CreateMQ {
private String number; // 开户编号
private String address; // 开户地
private Date accountDate; // 开户时间
private String desc; // 开户描述
}
@Data
public class DeliveredMQ {
private String uid; // 用户ID
private String orderId; // 订单号
private Date orderTime; // 下单时间
private Date sku; // 商品
private Date skuName; // 商品名称
private BigDecimal decimal; // 金额
}
@Data
public class OrderMQ {
private String uid; // 用户ID
private String sku; // 商品
private String orderId; // 订单ID
private Date createOrderTime; // 下单时间
}
要转换的实体类
@Data
public class RebateInfo {
private String userId; // 用户ID
private String bizId; // 业务ID
private Date bizTime; // 业务时间
private String desc; // 业务描述
}
适配器类
public class MQAdapter {
public static RebateInfo filter(String str,
Map<String, String> map) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, NoSuchFieldException {
Map jsonMap = JSONObject.parseObject(str, Map.class);
Set<String> keySet = map.keySet();
RebateInfo rebateInfo = new RebateInfo();
for (String key : keySet) {
RebateInfo.class
.getMethod("set" + key.substring(0, 1).toUpperCase() + key.substring(1),
RebateInfo.class.getDeclaredField(key).getType())
.invoke(rebateInfo,
JSONObject.parseObject(JSONObject.toJSONString(jsonMap.get(map.get(key))),
RebateInfo.class.getDeclaredField(key).getType()));
}
return rebateInfo;
}
}
测试类
public class TestAdapt {
public static void main(
String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, NoSuchFieldException {
HashMap<String, String> map = new HashMap<>();
map.put("userId", "number");
map.put("bizId", "number");
map.put("bizTime", "accountDate");
map.put("desc", "desc");
CreateMQ createMQ = CreateMQ.builder()
.number("100001")
.address("河北省.廊坊市.广阳区.大学里职业技术学院")
.accountDate(new Date())
.desc("在校开户")
.build();
String json = JSONObject.toJSONString(createMQ);
RebateInfo filter = MQAdapter.filter(json, map);
System.out.println(filter);
OrderMQ orderMq = new OrderMQ();
orderMq.setUid("100001");
orderMq.setSku("10928092093111123");
orderMq.setOrderId("100000890193847111");
orderMq.setCreateOrderTime(new Date());
HashMap<String, String> link02 = new HashMap<String, String>();
link02.put("userId", "uid");
link02.put("bizId", "orderId");
link02.put("bizTime", "createOrderTime");
RebateInfo filter02 = MQAdapter.filter(JSONObject.toJSONString(orderMq), link02);
System.out.println(filter02);
}
}