我是靠谱客的博主 畅快小熊猫,最近开发中收集的这篇文章主要介绍设计模式七:结构型-适配器模式,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

结构型模式:适配器模式

文章目录

    • 结构型模式:适配器模式
      • 适配器模式
        • 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;
}
@Override
public 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;
}
@Override
public 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;
}
@Override
public 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.Test
public 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.Test
public 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、适配器模式:总结

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

最后

以上就是畅快小熊猫为你收集整理的设计模式七:结构型-适配器模式的全部内容,希望文章能够帮你解决设计模式七:结构型-适配器模式所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(41)

评论列表共有 0 条评论

立即
投稿
返回
顶部