结构型模式:适配器模式

文章目录

  • 结构型模式:适配器模式
    • 适配器模式
      • 1、适配器模式:介绍
      • 2、适配器模式:模拟场景
      • 3、适配器模式:场景模拟工程
      • 4、适配器模式:代码实现
      • 4、适配器模式:总结

适配器模式

1、适配器模式:介绍

  • 适配器模式

    • 主要作用:把原本不兼容的接口通过适配修改做到统一,方便调用方使用
  • 在业务开发中,经常需要做不同接口的兼容,尤其是中台服务。中台需要把各个业务线的类型服务统一包装,再对外提供接口。

2、适配器模式:模拟场景

  • MQ消息体兼容场景

    • 营销系统配置后能把外部的MQ接入,这些MQ就像上面提到的注册开户信息、商品下单消息等。
    • 而适配器的思想也恰恰可以运用在这里。
    • 适配器不只可以适配接口,还可以适配一些属性信息

3、适配器模式:场景模拟工程

0、工程结构

lino-design-9.0
|——src|——main|--java|--com.lino.design|--mq|--CreateAccount.java|--OrderMq.java|--POPOrderDelivered.java|--service|--OrderService.java|--POPOrderService.java
  • 这里模拟三种不同类型的MQ消息

    • 注册开户MQ: CreateAccount
    • 内部订单MQ:OrderMq
    • 第三方订单MQ:POPOrderDelivered
  • 在消息体中有一些必要的字段,用户ID、时间、业务ID
    • 但是每个 MQ 的字段名称并不同,就像用户ID不同的MQ里也有不同的字段uId,userId 等一样
  • 还提供了两种不同类型的接口
    • OrderService:用于查询内部订单的下单数量
    • POPOrderService:用于查询第三方是否为首单

1、注册开户MQ

import com.alibaba.fastjson.JSON;
import java.util.Date;/*** @description: 注册开户MQ*/
public class CreateAccount {/*** 开户编号*/private String number;/*** 开户地*/private String address;/*** 开户时间*/private Date accountDate;/*** 开户描述*/private String desc;public String getNumber() {return number;}public void setNumber(String number) {this.number = number;}public String getAddress() {return address;}public void setAddress(String address) {this.address = address;}public Date getAccountDate() {return accountDate;}public void setAccountDate(Date accountDate) {this.accountDate = accountDate;}public String getDesc() {return desc;}public void setDesc(String desc) {this.desc = desc;}@Overridepublic String toString() {return JSON.toJSONString(this);}
}

2、内部订单MQ

import com.alibaba.fastjson.JSON;
import java.util.Date;/*** @description: 内部订单MQ*/
public class OrderMq {/*** 用户ID*/private String uid;/*** 商品编号*/private String sku;/*** 订单ID*/private String orderId;/*** 下单时间*/private Date createOrderTime;public String getUid() {return uid;}public void setUid(String uid) {this.uid = uid;}public String getSku() {return sku;}public void setSku(String sku) {this.sku = sku;}public String getOrderId() {return orderId;}public void setOrderId(String orderId) {this.orderId = orderId;}public Date getCreateOrderTime() {return createOrderTime;}public void setCreateOrderTime(Date createOrderTime) {this.createOrderTime = createOrderTime;}@Overridepublic String toString() {return JSON.toJSONString(this);}
}

3、第三方订单MQ

import com.alibaba.fastjson.JSON;
import java.math.BigDecimal;
import java.util.Date;/*** @description: 第三方订单MQ*/
public class POPOrderDelivered {/*** 用户ID*/private String uId;/*** 订单号*/private String orderId;/*** 下单时间*/private Date orderTime;/*** 商品编号*/private Date sku;/*** 商品名称*/private Date skuName;/*** 商品金额*/private BigDecimal decimal;public String getuId() {return uId;}public void setuId(String uId) {this.uId = uId;}public String getOrderId() {return orderId;}public void setOrderId(String orderId) {this.orderId = orderId;}public Date getOrderTime() {return orderTime;}public void setOrderTime(Date orderTime) {this.orderTime = orderTime;}public Date getSku() {return sku;}public void setSku(Date sku) {this.sku = sku;}public Date getSkuName() {return skuName;}public void setSkuName(Date skuName) {this.skuName = skuName;}public BigDecimal getDecimal() {return decimal;}public void setDecimal(BigDecimal decimal) {this.decimal = decimal;}@Overridepublic String toString() {return JSON.toJSONString(this);}
}

4、查询用户内部下单数量接口

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;/*** @description: 查询用户内部下单数量接口*/
public class OrderService {/*** 日志*/private Logger logger = LoggerFactory.getLogger(OrderService.class);/*** 查询用户下单数量** @param userId 用户ID* @return 下单数*/public long queryUserOrderCount(String userId) {logger.info("内部商家,查询用户的下单数量:{}", userId);return 10L;}
}

5、查询用户第三方下单首单接口

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;/*** @description: 查询用户第三方下单首单接口*/
public class POPOrderService {/*** 日志*/private Logger logger = LoggerFactory.getLogger(OrderService.class);/*** 查询用户第三方下单是否为首单** @param uId 用户ID* @return 是否为首单*/public boolean isFirstOrder(String uId) {logger.info("POP商家,查询用户的订单是否为首单:{}", uId);return true;}
}

4、适配器模式:代码实现

0、工程结构

lino-design-9.0
|——src|——main|--java|--com.lino.adapter|--impl|--InsideOrderServiceImpl.java|--POPOrderAdapterServiceImpl.java|--MQAdapter.java|--OrderAdapterService.java|--RebateInfo.java|——test|--java|--com.lino.test|--Test.java

  • 适配器模式的工程结构提供了两种适配方式的代码

    • 接口适配(OrderAdapterService
    • MQ适配(MQAdapter

1、MQ适配

  • 适配属性类
  • MQ 消息中会有多种多样的类型属性,虽然它们都同样提供给使用方
  • 但是如果都这样接入,那么当 MQ消息特别多时就会很耗时。所以
  • 定义通用的 MQ 消息体,后续把所有接入进来的消息进行统一的处理
/*** @description: MQ适配对象*/
public class RebateInfo {/*** 用户ID*/private String userId;/*** 业务ID*/private String bizId;/*** 业务时间*/private String bizTime;/*** 业务描述*/private String desc;public String getUserId() {return userId;}public void setUserId(String userId) {this.userId = userId;}public String getBizId() {return bizId;}public void setBizId(String bizId) {this.bizId = bizId;}public String getBizTime() {return bizTime;}public void setBizTime(String bizTime) {this.bizTime = bizTime;}public String getDesc() {return desc;}public void setDesc(String desc) {this.desc = desc;}
}
  • MQ消息统一适配类
  • 用于把不同类型的 MQ 中的各种属性映射成需要的属性并返回
    • 就像一个属性中有用户ID uId,将其映射到需要的 userId,做统一处理。
    • 这个处理过程需要把映射管理传递给 Map<String, String> link
    • 也就是准确描述了当前 MQ中某个属性名称,映射为指定的某个属性名称
    • 接收到的 MQ 消息基本是 JSON 格式,可以转换为 MAP 结构。
  • 最后,使用反射调用的方式对类型赋值
import com.alibaba.fastjson.JSON;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;/*** @description: MQ消息统一适配类*/
public class MQAdapter {/*** 映射属性** @param strJson 字段名称* @param link    属性集合* @return RebateInfo* @throws NoSuchMethodException* @throws InvocationTargetException* @throws IllegalAccessException*/public static RebateInfo filter(String strJson, Map<String, String> link) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {return filter(JSON.parseObject(strJson, Map.class), link);}/*** 映射属性** @param obj  集合* @param link 属性集合* @return RebateInfo* @throws NoSuchMethodException* @throws InvocationTargetException* @throws IllegalAccessException*/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;}
}

2、定义统一适配接口

  • 接口的实现需要完成此接口定义的方法,并将具体的逻辑包装到指定的类中,满足单一职责
/*** @description: 适配器接口*/
public interface OrderAdapterService {/*** 是否首单** @param uId 用户ID* @return boolean*/boolean isFirst(String uId);
}

3、分别实现两个不同的接口

  • 两种接口都实现类各自的判断方式

    • 尤其对于提供订单数量的接口,需要自己判断当前接到 MQ 时订单数量是否小于或等于1,以此判断是否为首单
  • 内部商品接口

import com.lino.adapter.OrderAdapterService;
import com.lino.design.service.OrderService;/*** @description: 内部商品接口实现类*/
public class InsideOrderServiceImpl implements OrderAdapterService {/*** 内部订单服务对象*/private OrderService orderService = new OrderService();public boolean isFirst(String uId) {return orderService.queryUserOrderCount(uId) <= 1;}
}
  • 第三方商品接口
import com.lino.adapter.OrderAdapterService;
import com.lino.design.service.POPOrderService;/*** @description: 第三方商品接口实现类*/
public class POPOrderAdapterServiceImpl implements OrderAdapterService {/*** 第三方订单服务对象*/private POPOrderService popOrderService = new POPOrderService();public boolean isFirst(String uId) {return popOrderService.isFirstOrder(uId);}
}

4、单元测试

import com.alibaba.fastjson.JSON;
import com.lino.adapter.MQAdapter;
import com.lino.adapter.OrderAdapterService;
import com.lino.adapter.RebateInfo;
import com.lino.adapter.impl.InsideOrderServiceImpl;
import com.lino.adapter.impl.POPOrderAdapterServiceImpl;
import com.lino.design.mq.CreateAccount;
import com.lino.design.mq.OrderMq;
import java.lang.reflect.InvocationTargetException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;/*** @description: 测试类*/
public class Test {@org.junit.Testpublic void testMQAdapter() throws ParseException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {SimpleDateFormat s = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");Date parse = s.parse("2020-06-01 23:20:16");CreateAccount create_account = new CreateAccount();create_account.setNumber("100001");create_account.setAddress("河北省.廊坊市.广阳区.大学里职业技术学院");create_account.setAccountDate(parse);create_account.setDesc("在校开户");HashMap<String, String> link01 = new HashMap<String, String>();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));System.out.println("");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));}@org.junit.Testpublic void testItfAdapter() {OrderAdapterService popOrderAdapterService = new POPOrderAdapterServiceImpl();System.out.println("判断首单,接口适配(POP):" + popOrderAdapterService.isFirst("100001"));OrderAdapterService insideOrderService = new InsideOrderServiceImpl();System.out.println("判断首单,接口适配(自营):" + insideOrderService.isFirst("100001"));}
}
  • 测试结果
mq.create_account(适配前){"accountDate":1591024816000,"address":"河北省.廊坊市.广阳区.大学里职业技术学院","desc":"在校开户","number":"100001"}
mq.create_account(适配后){"bizId":"100001","bizTime":"1591024816000","desc":"在校开户","userId":"100001"}mq.orderMq(适配前){"createOrderTime":1591024816000,"orderId":"100000890193847111","sku":"10928092093111123","uid":"100001"}
mq.orderMq(适配后){"bizId":"100000890193847111","bizTime":"1591024816000","userId":"100001"}15:17:56.640 [main] INFO  com.lino.design.service.OrderService - POP商家,查询用户的订单是否为首单:100001
判断首单,接口适配(POP):true
15:17:56.644 [main] INFO  com.lino.design.service.OrderService - 内部商家,查询用户的下单数量:100001
判断首单,接口适配(自营):false
  • 测试一结果

    • 同样的字段值在实现适配前后,分别有统一的字段属性
    • 除了反射的使用,还可以加入代理类,把映射的配置交给代理类,不需要手动创建类的每一个MQ
  • 测试二结果
    • 此时的接口已经统一包装,外部使用者不需要关心内部的具体逻辑。
    • 而且在调用时,只需要传入统一的参数即可,这样能满足适配的作用。

4、适配器模式:总结

  • 适配器模式可以让代码更干净、整洁,减少大量重复的判断和使用,同时也让代码更易于维护和扩展。

设计模式七:结构型-适配器模式相关推荐

  1. Java设计模式之结构型:适配器模式

    一.什么是适配器模式: 适配器模式主要用于将一个类的接口转化成客户端希望的目标类格式,使得原本不兼容的类可以在一起工作,将目标类和适配者类解耦:同时也符合"开闭原则",可以在不修改 ...

  2. 备战面试日记(3.3) - (设计模式.23种设计模式之结构型模式)

    本人本科毕业,21届毕业生,一年工作经验,简历专业技能如下,现根据简历,并根据所学知识复习准备面试. 记录日期:2022.1.9 大部分知识点只做大致介绍,具体内容根据推荐博文链接进行详细复习. 文章 ...

  3. Java设计模式之结构型:享元模式

    一.什么是享元模式: 享元模式通过共享技术有效地支持细粒度.状态变化小的对象复用,当系统中存在有多个相同的对象,那么只共享一份,不必每个都去实例化一个对象,极大地减少系统中对象的数量.比如说一个文本系 ...

  4. Java设计模式之结构型:组合模式

    前言: 我们对于上面两幅图片肯定非常熟悉,这两幅图片我们都可以看做是一个文件结构,对于这样的结构我们称之为树形结构.在数据结构中我们知道可以通过调用某个方法来遍历整个树,当我们找到某个叶子节点后,就可 ...

  5. Java设计模式之结构型:外观模式

    一.什么是外观模式: 外观模式通过对客户端提供一个统一的接口,用于访问子系统中的一群接口.使用外观模式有以下几点好处: (1)更加易用:使得子系统更加易用,客户端不再需要了解子系统内部的实现,也不需要 ...

  6. Java设计模式之结构型:代理模式

    前言: 我们一般在租房子时会去找中介,为什么呢?因为你对该地区房屋的信息掌握的不够全面,希望找一个更熟悉的人去帮你做:再比如我们打官司需要请律师,因为律师在法律方面有专长,可以替我们进行操作,表达我们 ...

  7. Java设计模式之结构型:装饰器模式

    一.什么是装饰器模式: 当需要对类的功能进行拓展时,一般可以使用继承,但如果需要拓展的功能种类很繁多,那势必会生成很多子类,增加系统的复杂性,并且使用继承实现功能拓展时,我们必须能够预见这些拓展功能, ...

  8. 设计模式 之 结构型模式

    设计模式 之 结构型模式 模式 & 描述 包括 结构型模式 这些设计模式关注类和对象的组合.继承的概念被用来组合接口和定义组合对象获得新功能的方式. 适配器模式(Adapter Pattern ...

  9. 设计模式之结构型模型

    设计模式之结构型模型 桥接模式 尽可能不要使用类的继承,而尽可能使用 合成/聚合 描述: 继承方法子类与父类的高依赖性限制了复用和程序的灵活性. 选择不同的接口实现选择不同的业务类型 import o ...

  10. 设计模式之结构型模式(5种)

    目录 结构型模式(Structural Pattern):怎么构造一个对象(行为.属性) 一.适配器模式 二.桥接模式(Bridge) 三.装饰者模式 设计模式在JAVA I/O库中的应用 案例 使用 ...

最新文章

  1. tensorflow-RNN和LSTM
  2. Redis 特殊数据类型 :Geospatial、Hyperloglog、Bitmap
  3. remmina连接xfce桌面的centos7
  4. 【转】1.1【MySQL】基本SQL语句大全
  5. python用法查询笔记_Python爬虫学习笔记(三)
  6. 4种Golang并发操作中常见的死锁情形
  7. 懂数据分析,你将比同龄人晋升快多少?
  8. Selenium关于滚动条的定位方法总结
  9. LNMP之PHP服务的安装(FastCGI方式)
  10. SAP操作手册之 IDOC基础篇
  11. 谷歌中国发布三国赤壁地图
  12. jquery进度条_jQuery进度栏
  13. 应用 | 使用小O地图制作热力分布图
  14. 未对销售组织 XXX 分销渠道 XX 语言 ZH 定义
  15. element-ui图标显示不出来问题
  16. 隐藏高德地图左下角LOGO
  17. 软件使用说明书V1.0发布
  18. ubuntu修改u盘权限_ubuntu系统下向U盘拷贝数据提示目标是只读的
  19. 微信小程序如何支持分享给朋友和分享到朋友圈?如何解决分享朋友圈灰色问题
  20. 修改计算机桌面壁纸影响计算机考试吗,计算机考试

热门文章

  1. 江西2021单招学校哪个学计算机好,2021年江西高职单招学校有哪些
  2. vue 利用element的Table 表格实现自制的穿梭框(可以高度自定义)
  3. 错误:DeprecationWarning: The default dtype for empty Series will be ‘object‘ instead of ‘float64‘ in
  4. 基于backtrader的唐奇安通道策略实现
  5. 致命错误,遇到在搜索预编译头文件时遇到意外的文件结尾
  6. POJ1029 题解
  7. uefi安装linux 知乎,安装Archlinux+UEFI启动
  8. 转贴通过造价工程师经验
  9. 每日一课 | Python数据可视化—认识坐标系
  10. Android : Camera之camx hal架构