适配器模式:顾名思义,适配,我们开发时候可能会出现不同属性名称的实体类,但是返回值必须是另一些属性名称的情况,这样其他部门或者前端开发也好不用改动了,这种情况下我们就可以用适配器模式,将原来的实体属性与新的前端需要的返回值的属性进行适配,就能返回期待的返回值。
以下就拿属性适配的例子说明:
我又画了一个丑丑的uml图,大家凑活看一下:
CreateAccount:创建账户实体类
UniteInfo:统一的返回值消息体,我们期望返回的字段
FieldAdapter:将属性消息进行适配的类
账户实体类
/*
*
* @Author df
* @Date 2020/10/30 16:59
*/
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;
}
}
统一成我们需要的的实体类
/*
* 统一的返回值消息体
* @Author df
* @Date 2020/10/30 16:03
*/
public class UniteInfo {
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;
}
}
重点来了,适配器类,转换成我们需要的实体
/**
* 属性消息体适配类
*
* @Author df
* @Date 2020/10/30 16:02
*/
public class FieldAdapter {
public static UniteInfo filter(String strJson, Map<String, String> link) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
return filter(JSON.parseObject(strJson, Map.class), link);
}
public static UniteInfo filter(Map obj, Map<String, String> link) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
UniteInfo rebateInfo = new UniteInfo();
for (String key : link.keySet()) {
Object val = obj.get(link.get(key));
UniteInfo.class.getMethod("set" + key.substring(0, 1).toUpperCase() + key.substring(1), String.class)
.invoke(rebateInfo, val.toString());
}
return rebateInfo;
}
}
测试一下外部调用:
/*
* 属性适配例子
* @Author df
* @Date 2020/10/30 16:58
*/
public class Test {
public static void main(String[] args) {
CreateAccount create_account = new CreateAccount();
create_account.setNumber("100001");
create_account.setAddress("北京");
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");
try {
UniteInfo rebateInfo = FieldAdapter.filter(JSON.toJSONString(create_account), link01);
System.out.println("mq.create_account(适配前)" + JSON.toJSONString(create_account));
System.out.println("mq.create_account(适配后)" + JSON.toJSONString(rebateInfo));
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
这样就适配成功了,既不会改动原有的任何类和属性,保证原有的类和属性如果还在调用也完全能够使用,这就是适配器的好处,当然如果返回的属性必须改,原有的属性类已经不需要了,那么可以直接将原有的属性改了而不需要适配器方式,看个人需求。
属性方式的适配的例子讲完了,那么接着说说接口适配的例子
接口例子:假如我要调用订单服务中的是否是首单,但是自营接口和自己订单接口起的方法名和返回值都不一样,此时我们就需要适配一下:
IOrderAdapterService: 抽象接口,用来定义统一的返回值和方法名
OrderAdapterServiceImpl: 具体自营适配实现
POPOrderAdapterServiceImpl:POP订单服务适配
OrderService:原自营订单实现
POPOrderService:原pop订单实现
IOrderAdapterService接口
/*
* 订单服务
* Target:目标接口类, 客户所期待的接口, 目标可以是具体的或者抽象的类, 也可以是接口
* @Author df
* @Date 2020/11/2 17:10
*/
public interface IOrderAdapterService {
boolean isFirst(String uId);
}
OrderAdapterServiceImpl 调用原接口OrderService方式适配
/**
* 自营订单服务适配,最终返回值是一致的
* Adapter:通过包装一个需要的支配器对象, 把原来的接口转换成目标接口
* @Author df
* @Date 2020/11/2 17:11
*/
public class OrderAdapterServiceImpl implements IOrderAdapterService {
private OrderService orderService = new OrderService();
@Override
public boolean isFirst(String uId) {
return orderService.isFirstOrder(uId);
}
}
POPOrderAdapterServiceImpl通过调用POPOrderService方式适配
/**
* POP订单服务适配,最终返回值是一致的
* Adapter:通过包装一个需要的支配器对象, 把原来的接口转换成目标接口
* @Author df
* @Date 2020/11/2 17:12
*/
public class POPOrderAdapterServiceImpl implements IOrderAdapterService {
private POPOrderService popOrderService = new POPOrderService();
@Override
public boolean isFirst(String uId) {
return popOrderService.queryUserOrderCount(uId) <= 1;
}
}
OrderService是返回boolean
/**
* 自营:订单各个平台给的方法名不一样,返回值不一样
* Adaptee:需要适配的类
* @Author df
* @Date 2020/11/2 17:08
*/
public class OrderService {
boolean isFirstOrder(String userId) {
return true;
}
}
POPOrderService是通过查询订单数量判断是不是首单,所以返回long类型
public class POPOrderService {
Long queryUserOrderCount(String userId) {
return 1L;
}
}
测试一下:
/*
* 接口适配示例
* @Author df
* @Date 2020/11/2 17:17
*/
public class Test {
public static void main(String[] args) {
// 通过适配器给接口同一个方法名称并且统一了同一个返回值
IOrderAdapterService orderAdapterService = new OrderAdapterServiceImpl();
System.out.println("适配器:判断首单,接口适配(自营)" + orderAdapterService.isFirst("100001"));
IOrderAdapterService popOrderService = new POPOrderAdapterServiceImpl();
System.out.println("适配器:判断首单,接口适配(POP)" + popOrderService.isFirst("100001"));
}
}
以上就是接口适配的示例,设计模式有一些类似语法,比如各个角色啊,必须满足什么条件啊,但是我又记不住就算我现在记下了最终又忘了,按照理解知道怎么写就好,但是也看了下模式结构:
- Target:目标抽象类 ,客户所期待的接口, 目标可以是具体的或者抽象的类, 也可以是接口,本示例就是IOrderAdapterService
- Adapter:适配器类,通过包装一个需要的支配器对象, 把原来的接口转换成目标接口,本案例为OrderAdapterServiceImpl和POPOrderAdapterServiceImpl
- Adaptee:适配者类,需要适配的类,本案例为OrderService和POPOrderService
- Client:客户类,由谁调用,本案例main方法
本示例学习bugstack设计模式而总结!