栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

设计模式七:结构型-适配器模式

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

设计模式七:结构型-适配器模式

结构型模式:适配器模式

文章目录
    • 结构型模式:适配器模式
      • 适配器模式
        • 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;


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;


public class OrderMq {

    
    private String uid;

    
    private String sku;

    
    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;


public class POPOrderDelivered {
    
    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;


public class OrderService {
    
    private Logger logger = LoggerFactory.getLogger(OrderService.class);
    
    public long queryUserOrderCount(String userId) {
        logger.info("内部商家,查询用户的下单数量:{}", userId);
        return 10L;
    }
}

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

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


public class POPOrderService {
    
    private Logger logger = LoggerFactory.getLogger(OrderService.class);
    
    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 消息体,后续把所有接入进来的消息进行统一的处理
public class RebateInfo {
    
    private String userId;
    
    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 link
    • 也就是准确描述了当前 MQ中某个属性名称,映射为指定的某个属性名称
    • 接收到的 MQ 消息基本是 JSON 格式,可以转换为 MAP 结构。
  • 最后,使用反射调用的方式对类型赋值
import com.alibaba.fastjson.JSON;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;


public class MQAdapter {

    
    public static RebateInfo filter(String strJson, Map link) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        return filter(JSON.parseObject(strJson, Map.class), link);
    }

    
    public static RebateInfo filter(Map obj, Map 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、定义统一适配接口

  • 接口的实现需要完成此接口定义的方法,并将具体的逻辑包装到指定的类中,满足单一职责
public interface OrderAdapterService {
    
    boolean isFirst(String uId);
}

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

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

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

import com.lino.adapter.OrderAdapterService;
import com.lino.design.service.OrderService;


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;


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;


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 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));

        System.out.println("");

        OrderMq orderMq = new OrderMq();
        orderMq.setUid("100001");
        orderMq.setSku("10928092093111123");
        orderMq.setOrderId("100000890193847111");
        orderMq.setCreateOrderTime(parse);

        HashMap link02 = new HashMap();
        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、适配器模式:总结
  • 适配器模式可以让代码更干净、整洁,减少大量重复的判断和使用,同时也让代码更易于维护和扩展。
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/318749.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号