适配器模式(Adapter)指的是将一个类的接口转换成另一个可以兼容的接口。比如我们日常生活中的转换头、古早时期使用的电池万能充,就相当于程序中使用的适配器模式。

一、适配器模式的介绍

1.1 适配器模式的结构

适配器模式模式主要分为类结构型模式和对象结构型模式两种:

1.1.1 类适配器模式

类适配器模式通过多重继承,将一个接口与另一个接口进行匹配。而对于一些面向对象语言如C#、Java不支持多重继承,那么我们就可以继承一个类,同时实现多个接口来达到适配器的效果。如下图所示:

  • Adaptee:适配者类,它是需要被访问的、需要被适配的组件
  • Target:目标接口,当前系统业务所使用的接口,可以是抽象类或接口
  • Adapter:适配器类,通过继承和实现目标接口,让客户端按照目标接口的方法访问适配者
  • Client:客户端,适配器的使用者

1.1.2 对象适配器模式

对象适配器模式相对于类适配器的不同点在于,对象适配器中适配者类和适配器类的耦合度要更低。如下图所示:

  • Adaptee:适配者类,它是需要被访问的、需要被适配的组件
  • Target:目标接口,当前系统业务所使用的接口,可以是抽象类或接口
  • Adapter:适配器类,通过聚合和实现目标接口,让客户端按照目标接口的方法访问适配者
  • Client:客户端,适配器的使用者

1.2 适配器模式的应用实现

我们可以根据上面两种模式分别进行实现:

1.2.1 类适配器实现代码

//适配者类
public class Adaptee {public void specificRequest(){System.out.println("我是适配者类");}
}
//目标接口
public interface Target {public void request();
}
//适配器类
public class Adapter extends Adaptee implements Target{@Overridepublic void request() {specificRequest();}
}
//客户端类
public class Client {public static void main(String[] args) {Target target = new Adapter();target.request();}
}

1.2.2 对象适配器实现代码

//适配者类
public class Adaptee {public void specificRequest(){System.out.println("我是适配者类");}
}
//对象适配器类
public class ObjectAdapter implements Target{private Adaptee adaptee;public ObjectAdapter(Adaptee adaptee) {this.adaptee = adaptee;}@Overridepublic void request() {adaptee.specificRequest();}
}
//客户端类
public class Client {public static void main(String[] args) {Adaptee adaptee = new Adaptee();Target target = new ObjectAdapter(adaptee);target.request();}
}

我们发现,对象适配器模式是在适配器类中引入了适配者,这样就利用聚合的方式将两个类连接在一起。而根据设计原则,聚合优先于继承,所以在我们日常的使用中,应该多选择对象适配器模式。

三、适配器的应用场景

3.1 MyBatis中的日志适配应用

在MyBatis 中的典型代表是日志模块,比如其中适配了slf4jApache Commons LoggingLog4j2JDK logging等的日志类型,下面来看看具体实现:

//统一的Log接口
public interface Log {boolean isDebugEnabled();boolean isTraceEnabled();void error(String s, Throwable e);void error(String s);void debug(String s);void trace(String s);void warn(String s);
}

MyBatis 定义了多个日志类型的适配器,以Log4j2实现为例:

public class Log4j2Impl implements Log {private final Log log;public Log4j2Impl(String clazz) {Logger logger = LogManager.getLogger(clazz);if (logger instanceof AbstractLogger) {log = new Log4j2AbstractLoggerImpl((AbstractLogger) logger);} else {log = new Log4j2LoggerImpl(logger);}}@Overridepublic boolean isDebugEnabled() {return log.isDebugEnabled();}@Overridepublic boolean isTraceEnabled() {return log.isTraceEnabled();}@Overridepublic void error(String s, Throwable e) {log.error(s, e);}@Overridepublic void error(String s) {log.error(s);}@Overridepublic void debug(String s) {log.debug(s);}@Overridepublic void trace(String s) {log.trace(s);}@Overridepublic void warn(String s) {log.warn(s);}
}

所以在项目添加Log4j2后,就可以直接使用它打印MyBatis的日志信息。

3.2 营销系统中的各种MQ消息或接口的适配应用

3.2.1 代码工程介绍

在营销系统中系统会接收各种各样的MQ消息或接口,比如有邀请用户、内部订单、外部订单的消息。这个时候就可以利用适配器模式对这些MQ消息进行适配。如下图所示:

代码目录结构如下:

src├─main│  ├─java│  │  └─cn│  │      └─ethan│  │          └─design│  │              │  OrderAdapterService.java│  │              ││  │              ├─adapter│  │              │      MQAdapter.java│  │              │      RebateInfo.java│  │              ││  │              ├─impl│  │              │      InsideOrderService.java│  │              │      POPOrderAdapterService.java│  │              ││  │              ├─mq│  │              │      create_account.java│  │              │      OrderMq.java│  │              │      POPOrderDelivered.java│  │              ││  │              └─service│  │                      OrderService.java│  │                      POPOrderService.java│  ││  └─resources└─test└─javaApiTest.java
  • mq和service 目录中是MQ消息类型和提供的服务
  • adapter目录中是适配MQ消息体和一些属性信息
  • service目录中是模拟的接口适配

3.2.2 代码实现

  1. MQ消息体和属性信息适配
//MQ属性信息适配
public class RebateInfo {private String userId;private String bizId;private Date bizTime;private String desc;
}
//MQ消息体适配,将不同MQ的各种属性,映射成我们需要的属性并返回
public class MQAdapter {public static RebateInfo filter(String strJson, Map<String, String> link) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {return filter(JSON.parseObject(strJson, Map.class), link);}public static RebateInfo filter(Map obj, Map<String, String> link) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {RebateInfo rebateInfo = new RebateInfo();for (String key : link.keySet()) {Object val = obj.get(link.get(key));RebateInfo.class.getMethod("set" + key.substring(0, 1).toUpperCase() + key.substring(1), String.class).invoke(rebateInfo, val.toString());}return rebateInfo;}
}
  1. 接口服务适配

提供了两个接口,一个是直接判断是否首单,另外一个需要根据订单数量进行判断。定义统一的适配接口:

//统一适配接口
public interface OrderAdapterService {boolean isFirst(String uId);
}
//内部商品接口
public class InsideOrderService implements OrderAdapterService {private OrderService orderService = new OrderService();@Overridepublic boolean isFirst(String uId) {return orderService.queryUserOrderCount(uId) <= 1;}
}
//第三方商品接口
public class POPOrderAdapterService implements OrderAdapterService {private POPOrderService popOrderService = new POPOrderService();@Overridepublic boolean isFirst(String uId) {return popOrderService.isFirstOrder(uId);}
}

最后进行测试

public class ApiTest {@Testpublic void test_MQAdapter() throws ParseException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");Date parse = simpleDateFormat.parse("2022-3-29 23:09:17");create_account create_account = new create_account();create_account.setNumber("100001");create_account.setAddress("河北省.廊坊市.广阳区.大学里职业技术学院");create_account.setAccountDate(parse);create_account.setDesc("在校开户");HashMap<String, String> link01 = new HashMap<>();link01.put("userId", "number");link01.put("bizId", "number");link01.put("bizTime", "accountDate");link01.put("desc", "desc");RebateInfo rebateInfo01 = MQAdapter.filter(create_account.toString(), link01);System.out.println("mq.create_account(适配前)" + create_account.toString());System.out.println("mq.create_account(适配后)" + JSON.toJSONString(rebateInfo01));OrderMq orderMq = new OrderMq();orderMq.setUid("100001");orderMq.setSku("10928092093111123");orderMq.setOrderId("100000890193847111");orderMq.setCreateOrderTime(parse);HashMap<String, String> link02 = new HashMap<String, String>();link02.put("userId", "uid");link02.put("bizId", "orderId");link02.put("bizTime", "createOrderTime");RebateInfo rebateInfo02 = MQAdapter.filter(orderMq.toString(), link02);System.out.println("mq.orderMq(适配前)" + orderMq.toString());System.out.println("mq.orderMq(适配后)" + JSON.toJSONString(rebateInfo02));}@Testpublic void test_itfAdapter() {OrderAdapterService popOrderAdapterService = new POPOrderAdapterService();System.out.println("判断首单,接口适配(POP):" + popOrderAdapterService.isFirst("100001"));OrderAdapterService insideOrderService = new InsideOrderService();System.out.println("判断首单,接口适配(自营):" + insideOrderService.isFirst("100001"));}}

测试结果如下:

mq.create_account(适配前){"accountDate":1648566557000,"address":"河北省.廊坊市.广阳区.大学里职业技术学院","desc":"在校开户","number":"100001"}
mq.create_account(适配后){"bizId":"100001","bizTime":1591077840669,"desc":"在校开户","userId":"100001"}
mq.orderMq(适配前){"createOrderTime":1648566557000,"orderId":"100000890193847111","sku":"10928092093111123","uid":"100001"}
mq.orderMq(适配后){"bizId":"100000890193847111","bizTime":1591077840669,"userId":"100001"}
---------------------------------------------------------------------------------
13:12:20.335 [main] INFO  c.e.design.service.POPOrderService - POP商家,查询⽤户的订单是否为⾸单:100001
判断首单,接口适配(POP):true
13:12:20.339 [main] INFO  cn.ethan.design.service.OrderService - 自营商家,查询用户的订单是否为首单:100001
判断首单,接口适配(自营):false

参考资料

《重学Java设计模式》

https://kaiwu.lagou.com/course/courseInfo.htm?courseId=59#/detail/pc?id=1773

关于Java适配器模式,你该了解这些相关推荐

  1. java什么是适配器类?作用是什么?_浅谈Java适配器模式

    假期刚结束不久,也没什么好写的,今天就水下文章,讲讲设计模式对开发时的影响,做开发到现在,感觉设计模式对开发的影响还是挺大的. 这次就简单谈谈适配器模式.可能适配器模式感觉比较鸡肋,但是用到的地方还挺 ...

  2. java 适配器模式示例

    java 适配器模式示例 一.定义 ​ 适配器模式用于解决接口间的兼容问题. ​ 当我们需要使用某个类提供的接口,但是这个接口与现在的系统需求不符,由于该接口是由第三方提供的,或者是已经在生产上跑了很 ...

  3. java适配器模式_适配器模式的设计,你get到了吗?

    作者:rabbitwfly 假如我们又这样软件系统,我们希望它能够和一个新的库搭配使用,但是这个库所提供的接口与我们的软件系统不兼容,我们不想改变现有代码就能解决这个问题,怎么办?这个时候我们就需要将 ...

  4. java适配器模式例子_java适配器模式实例解析

    适配器模式作为一种结构型设计模式,在软件系统设计开发中使用到的频率非常之高,深受开发人员的青睐.本文会讲解适配器模式的知识要点,以及在实际项目中所使用的真实案例. 1.适配器模式概述: 适配器模式(A ...

  5. Java适配器模式详解

    文章目录 设计模式分类 适配器模式 `实例:` 小结 编程之外 设计模式分类 设计模式分为三种类型,共23种:这里先暂时只分享适配器模式的学习 创建型模式:单例模式.抽象工厂模式.建造者模式.工厂模式 ...

  6. 浅谈JAVA适配器模式

    最近辞职在家,有空写写博客.当然本人也会时不时的发疯在开头写一些有的没的,别介意. 当把最爱的那个人放手时,所有的东西也就释怀了. JAVA适配器的由来:基于类只能继承一个父类.却可以实现多个接口的限 ...

  7. Java适配器模式详解和实际应用

    一.了解适配器模式 1.什么是适配器模式 适配器模式将一个类的接口转换成另一种接口,让原本接口不兼容的类可以兼容.这种模式的主要作用就是把原本不兼容的接口,通过适配修改做到统一. 这个模式可以通过创建 ...

  8. Java适配器模式(adapter)

    文章目录 适配器模式 什么是适配器模式 模式中的角色 适配器的实现方式 1.类适配器方式 2.对象适配器方式 工作中的场景   本文介绍下java设计模式中的适配器模式 适配器模式 什么是适配器模式 ...

  9. java适配器模式火鸡变凤凰是,结合案例深入解析适配器模式(一)

    一.基本概念 将一个类的接口,转换成客户期望的另一个接口.适配器让原本接口不兼容的类可以合作无间. 可以将适配器理解为我们日常用的电脑充电器: 家庭电压为220V,而电脑充电频率是20V左右,所以需要 ...

  10. java适配器模式 场景_Java设计模式之《适配器模式》及应用场景

    适配器就是一种适配中间件,它存在于不匹配的二者之间,用于连接二者,将不匹配变得匹配,简单点理解就是平常所见的转接头,转换器之类的存在. 适配器模式有两种:类适配器.对象适配器.接口适配器 前二者在实现 ...

最新文章

  1. #2002-The server is not responding (or the local MySQL server's socket is not correctly configur
  2. 增加CentOS File Descriptors
  3. laravel-mix 使用
  4. 如何找出nginx配置文件的所在位置?
  5. html弹出文本输入框,Windows API 弹出文本框输入的内容
  6. nginx php mysql一些常用命令(windows linux)
  7. vue项目+富文本编辑器ueditor - 资源篇
  8. java 阻塞 直到完成_完成所有提交的任务后关闭Java执行程序而不会阻塞
  9. yolo-v5连接手机摄像头实时检测的步骤
  10. 【drawio笔记】为Confluence Cloud使用自定义字体
  11. win7 IIS误删default website的恢复方法
  12. 【2012求职经历】应届生求职经历
  13. 分享一些嵌入式相关的开源项目
  14. 大白菜安装系统两种方式
  15. Python_高级特性
  16. 企业文件分享/共享有哪些安全方式
  17. 2020.05.26
  18. springAop学习笔记(二,springboot进本配置和使用)
  19. R 关于NA的处理办法
  20. NLP底层技术之句法分析

热门文章

  1. 电子邮件群发工资条的方法
  2. influxdb数据过期_influxdb 清空数据库
  3. 实时查看Starlink在轨卫星、地面站数目和分布情况的有趣网站
  4. 超详细 excel 基础知识
  5. 《网络运维 - 基础知识》
  6. Node.js使用jszip实现文件夹操作
  7. ceph部署-纠删码
  8. 前端接收pdf文件_前端利用pdfobject.js处理pdf文件
  9. 【Java · 类加载】类加载器
  10. matlab ode45 二阶微分方程,ode45解二阶微分方程