其他分享
首页 > 其他分享> > 结构型模式之适配器模式的案例示范

结构型模式之适配器模式的案例示范

作者:互联网

1. 介绍

在业务开发中我们经常需要做不同接口的兼容,尤其是中台服务,中台需要把各个业务线的各种类型服务做统一包装,再对外提供接口进行使用。

2. 案例场景模拟

在这里插入图片描述

随着业务不断发展,当基础的系统逐步成型之后,业务运营就需要开始做用户的拉新和促活,从而保证DUA的增速以及最终ROI转换。

这里模拟一个营销系统,例如:你邀请一个用户开户,或者邀请一个用户下单,那么平台就会给你返利,多邀多得。同时随着拉新的量越来越多,开始设置每月下单就会给首单奖励等等各种营销场景。

那么这个时候做这样一个系统就会接收各种各样的MQ消息或者接口,如果一个个开发,就会耗费很大的成本,也不利于后期的拓展。此时,希望有一个系统可以配置一下就把外部的MQ接入进行,这些MQ就像上面提到的可能是注册开户消息、商品下单消息等等。

而适配器的思想恰恰可以运用到这里,并且适配器不止可以适配接口,也可以适配一些属性信息。

场景模拟工程

org.itstack.demo.design
----mq
	----create_account.java
	----OrderMq.java
	----POPOrderDelivered.java
----service
	----OrderService.java
	----POPOrderService.java

场景简述

1、注册开户MQ

public class create_account {
    private String number; // 开户编号
    private String address; // 开户地
    private Date accountDate; // 开户时间
    private String desc; // 开户描述
    // ... get/set
}

2、内部订单MQ

public class OrderMq {
    private String uid; // 用户ID
    private String sku; // 商品
    private String orderId; // 订单ID
    private Date createOrderTime; // 下单时间
    // ... get/set
}

3、第三方订单MQ

public class POPOrderDelivered {
    private String uId; // 用户ID
    private String orderId; // 订单号
    private Date orderTime; // 下单时间
    private Date sku; // 商品
    private Date skuName; // 商品名称
    private BigDecimal decimal; // 金额
    // ... get/set
}

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

public class OrderService {
    private Logger logger =LoggerFactory.getLogger(POPOrderService.class);
    public long queryUserOrderCount(String userId){
        logger.info("自营,查询用户的的订单是否为首单:{}", userId);
        return 10L;
    }
}

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

public class POPOrderService {
    private Logger logger = LoggerFactory.getLogger(POPOrderService.class);
    public boolean isFirstOrder(String uId) {
        logger.info("POP商家,查询用户订单是否为首单:{}", uId);
        return true;
    }
}

以上是不同的MQ以及不同接口的一个体现,后面我们将使用这样的MQ消息和接口,给他们做相应的适配。

3. 一坨代码实现

大部分时候接MQ消息都是创建一个类用于消费,通过转换他的MQ消息属性给自己的方法。

接下来先体现以下这种方式的实现模拟,但是这样的实现有一个很大的问题是,当MQ消息越来越多后,甚至几十几百之后,你作为中台要怎么优化呢?

工程结构

org.itstack.demo.design
----create_accountMqService.java
----OrderMqService.java
----POPOrderDeliveredService.java

目前需要接收三种MQ消息,所以对应了3个类,和我们平时的代码习惯几乎一样。如果MQ量不大,这样的写法没什么问题,但是随着数量的增加,就需要考虑用一些设计模式来优化。

MQ接收消息实现

public class create_accountMqService {
    public void onMessage(String message) {
        create_account mq = JSON.parseObject(message,create_account.class);
        mq.getNumber();
        mq.getAccountDate();
        // ... 业务逻辑
    }
}

其他两组MQ消息都是一样,就不一一展示了。

4. 适配器模式重构代码

适配器模式要解决的主要问题就是多种差异化类型的接口做统一的输出,在本文中我们还会体现出一个多种MQ接收和使用MQ的场景,来把不同类型的消息做统一的处理,便于减少后续对MQ接收。

如果之前没接触过MQ消息,建议先去了解一下,不过就算不了解,也不会影响对思路的体会。

在者,本文所展示的MQ兼容的核心部分,也就是处理适配不同的类型字段。而如果我们接收MQ后,在配置不同的消费类时,如果不希望一个个开发类,name就可以使用代理类的方式进行处理。

工程结构

org.itstack.demo.design
----impl
	----InsideOrderService.java
	----POPOrderAdapterService.java
----MQAdapter.java
----OrderAdapterService.java
----RebateInfo.java

适配器模式结构

在这里插入图片描述

代码实现(MQ消息适配)

1、统一的MQ消息体

public class RebateInfo {
    private String userId; // 用户ID
    private String bizId; // 业务ID
    private Date bizTime; // 业务时间
    private String desc; // 业务描述
    // ... get/set
}

2、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);
    }
    // 转化成统一的MQ对象
    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;
    }
}

3、测试适配类

编写单元测试类

@Test
public void test_MQAdapter() throws NoSuchMethodException,IllegalAccessException, InvocationTargetException {
    // 1
    create_account create_account = new create_account();
    create_account.setNumber("100001");
    create_account.setAddress("杭州市,xx大学");
    create_account.setAccountDate(new Date());
    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));
	// 2
    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 rebateInfo02 = MQAdapter.filter(orderMq.toString(),link02);
    System.out.println("mq.orderMq(前)" + orderMq.toString());
    System.out.println("mq.orderMq(后)" +JSON.toJSONString(rebateInfo02));
}

测试结果

mq.create_account(前){"accountDate":1591024816000,"address":"杭州市,xx大学","desc":"在校开户","number":"100001"}
mq.create_account(后){"bizId":"100001","bizTime":1591077840669,"desc":"在校开户","userId":"100001"}
mq.orderMq(前)
{"createOrderTime":1591024816000,"orderId":"100000890193847111","sku":"1092
8092093111123","uid":"100001"}
mq.orderMq(后)
{"bizId":"100000890193847111","bizTime":1591077840669,"userId":"100001"}
 
Process finished with exit code 0

代码实现(接口使用适配)

模拟场景下,增加条件:只有首单用户才给奖励。

那么就需要对此种方式进行限制,而此时MQ中并没有判断首单的属性。只能通过接口进行查询,而拿到的接口如下:

接口描述
queryUserOrderCount(String userId)出参long,查询订单数量
isFirstOrder(String uId)出参boolean,判断是否首单

1、定义统一适配接口

public interface OrderAdapterService {
	boolean isFirst(String uId);
}

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

内部商品接口

public class InsideOrderService implements OrderAdapterService {
    private OrderService orderService = new OrderService();
    
    public boolean isFirst(String uId) {
    	return orderService.queryUserOrderCount(uId) <= 1;
    }
}

第三方商品接口

public class POPOrderAdapterServiceImpl implements OrderAdapterService {
    private POPOrderService popOrderService = new POPOrderService();
    
    public boolean isFirst(String uId) {
    	return popOrderService.isFirstOrder(uId);
    }
}

3、测试适配类

单元测试类

@Test
public void test_itfAdapter() {
    OrderAdapterService popOrderAdapterService = new POPOrderAdapterServiceImpl();
    System.out.println("适配(POP):" + popOrderAdapterService.isFirst("100001"));
    OrderAdapterService insideOrderService = new InsideOrderService();
    System.out.println("适配(自营):" + insideOrderService.isFirst("100001"));
}

测试结果

23:25:47.076 [main] INFO o.i.d.design.service.POPOrderService - POP查询订单:100001
适配(POP):true
23:25:47.079 [main] INFO o.i.d.design.service.POPOrderService - 自营,查询订单:100001
适配(自营):false

Process finished with exit code 0

5. 总结

标签:String,适配,适配器,接口,private,MQ,模式,public,结构型
来源: https://blog.csdn.net/HUBA_yosa/article/details/122261203