赞
踩
适配器模式(Adapter)指的是将一个类的接口转换成另一个可以兼容的接口。比如我们日常生活中的转换头、古早时期使用的电池万能充,就相当于程序中使用的适配器模式。
适配器模式模式主要分为类结构型模式和对象结构型模式两种:
类适配器模式通过多重继承,将一个接口与另一个接口进行匹配。而对于一些面向对象语言如C#、Java不支持多重继承,那么我们就可以继承一个类,同时实现多个接口来达到适配器的效果。如下图所示:
Adaptee
:适配者类,它是需要被访问的、需要被适配的组件Target
:目标接口,当前系统业务所使用的接口,可以是抽象类或接口Adapter
:适配器类,通过继承和实现目标接口,让客户端按照目标接口的方法访问适配者Client
:客户端,适配器的使用者对象适配器模式相对于类适配器的不同点在于,对象适配器中适配者类和适配器类的耦合度要更低。如下图所示:
Adaptee
:适配者类,它是需要被访问的、需要被适配的组件Target
:目标接口,当前系统业务所使用的接口,可以是抽象类或接口Adapter
:适配器类,通过聚合和实现目标接口,让客户端按照目标接口的方法访问适配者Client
:客户端,适配器的使用者我们可以根据上面两种模式分别进行实现:
//适配者类 public class Adaptee { public void specificRequest(){ System.out.println("我是适配者类"); } } //目标接口 public interface Target { public void request(); } //适配器类 public class Adapter extends Adaptee implements Target{ @Override public void request() { specificRequest(); } } //客户端类 public class Client { public static void main(String[] args) { Target target = new Adapter(); target.request(); } }
//适配者类 public class Adaptee { public void specificRequest(){ System.out.println("我是适配者类"); } } //对象适配器类 public class ObjectAdapter implements Target{ private Adaptee adaptee; public ObjectAdapter(Adaptee adaptee) { this.adaptee = adaptee; } @Override public void request() { adaptee.specificRequest(); } } //客户端类 public class Client { public static void main(String[] args) { Adaptee adaptee = new Adaptee(); Target target = new ObjectAdapter(adaptee); target.request(); } }
我们发现,对象适配器模式是在适配器类中引入了适配者,这样就利用聚合的方式将两个类连接在一起。而根据设计原则,聚合优先于继承,所以在我们日常的使用中,应该多选择对象适配器模式。
在MyBatis 中的典型代表是日志模块,比如其中适配了slf4j
、Apache Commons Logging
、Log4j2
和JDK 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); } } @Override public boolean isDebugEnabled() { return log.isDebugEnabled(); } @Override public boolean isTraceEnabled() { return log.isTraceEnabled(); } @Override public void error(String s, Throwable e) { log.error(s, e); } @Override public void error(String s) { log.error(s); } @Override public void debug(String s) { log.debug(s); } @Override public void trace(String s) { log.trace(s); } @Override public void warn(String s) { log.warn(s); } }
所以在项目添加Log4j2
后,就可以直接使用它打印MyBatis的日志信息。
在营销系统中系统会接收各种各样的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 └─java ApiTest.java
//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; } }
提供了两个接口,一个是直接判断是否首单,另外一个需要根据订单数量进行判断。定义统一的适配接口:
//统一适配接口 public interface OrderAdapterService { boolean isFirst(String uId); } //内部商品接口 public class InsideOrderService implements OrderAdapterService { private OrderService orderService = new OrderService(); @Override public boolean isFirst(String uId) { return orderService.queryUserOrderCount(uId) <= 1; } } //第三方商品接口 public class POPOrderAdapterService implements OrderAdapterService { private POPOrderService popOrderService = new POPOrderService(); @Override public boolean isFirst(String uId) { return popOrderService.isFirstOrder(uId); } }
最后进行测试
public class ApiTest { @Test public 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)); } @Test public 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
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。