适配器模式介绍
在计算机编程中,适配器模式将一个类的接口适配成用户所期待的。一个适配允许通常因为接口不兼容而不能在一起工作的类工作在一起,做法是将类自己的接口包裹在一个已存在的类中。其实适配器模式一开始在系统设计的时候是不会去考虑的,大多的是在后期的使用中出现一些问题,才会去设计,算是一种“补救”。
例子解说
上一篇说了责任链模式,用餐饮系统下单来作为一个例子,今天继续用餐饮系统来说适配器模式,因为一开始餐饮系统只是堂食,外卖这些也是基于自己的餐饮系统,但是随着餐厅的发展壮大,更多的人开始选择外卖,这样的话,收银员要同时在几个平台上进行操作,一直切换就很麻烦,数据不好统计,于是接入了美团外卖和饿了么外卖,在美团或者饿了么上下单了以后,数据会统计到我们自己的餐饮系统里面来,因为之前我们的系统已经有了一个接口,但是它不兼容饿了么和美团的接口,但是在下单以后,都会保存订单信息,也会更新菜品库存等信息,于是开始设计一个适配的接口。
伪代码实现
首先我们先看一看原始的下单接口,包括了下单placeAnOrder(),保存订单saveOrder(),更新菜品库存updateStock(),
public class OrderServiceImpl implements IOrderService{
@Override
public String saveOrder(OrderInfo orderInfo) {
return save(orderInfo);
}
@Override
public boolean updateStock(List<Integer> list) {
return updateBatchByIdList(list);
}
@Override
public String placeAnOrder(OrderInfo orderInfo) {
//做一些其它的操作
updateStock(orderInfo.getFoodIdList());
return saveOrder(orderInfo);
}
}
我们看一下饿了么订单实体,美团订单实体和我们餐饮系统的订单实体,这里省略很多字段,只做一个简单的举例。
本系统订单实体
@Data
public class OrderInfo {
String orderId;
String people;
String orderTime;
String tel;
List<Integer> foodIdList;
}
饿了么订单实体
@Data
public class ElemeOrder {
String createdAt;
String id;
String phoneList;
String consignee;
List<Integer> foodIdList;
}
美团订单实体
@Data
public class MeituanOrder {
String orderId;
String recipientName;
String recipientPhone;
Date ctime;
List<Integer> foodIdList;
}
我们新增一个第三方订单接口,有美团订单接口和饿了么订单接口。
public interface IThirdOrderService {
String meiTuanOrder(MeituanOrder meituanOrder , Map<String,String> filedMappingList);
String elemeOrder(ElemeOrder elemeOrder , Map<String,String> filedMappingList);
}
然后编写一个适配器类,继承原始的订单接口和实现第三方订单接口,从下面我们可以看出我们编写了一个converter方法对字段对象进行了转换,因为本系统订单的字段和第三方接口的字段不一样(比如本系统的订单id为orderId,而饿了么的订单id为id),所以需要进行转换,这里参考了《重学Java设计模式》小傅哥的写法。
public class ThirdOrderAdapter extends OrderServiceImpl implements IThirdOrderService {
@Override
public String meiTuanOrder(MeituanOrder meituanOrder , Map<String,String> filedMappingList) {
return super.placeAnOrder(converter(meituanOrder,filedMappingList));
}
@Override
public String elemeOrder(ElemeOrder elemeOrder , Map<String,String> filedMappingList) {
return super.placeAnOrder(converter(elemeOrder,filedMappingList));
}
/**
* 转换器
* @param obj
* @param filedMappingList
* @return
*/
private OrderInfo converter(Object obj , Map<String , String> filedMappingList){
String jsonString = JSON.toJSONString(obj);
Map object = JSON.parseObject(jsonString, Map.class);
OrderInfo orderInfo = new OrderInfo();
filedMappingList.forEach((key,value) -> {
Object o = object.get(filedMappingList.get(key));
try {
OrderInfo.class.getMethod("set" + key.substring(0,1).toUpperCase() + key.substring(1),String.class).invoke(orderInfo,o.toString());
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
e.printStackTrace();
}
});
return orderInfo;
}
}
使用饿了么或者美团下单,首先将两个实体的字段进行对应,放进一个map中,再根据需求调用对应的第三方接口,对于实体转换,我们也可以使用其他方式进行转换,比如MapStruct,然后稍微修改一下代码便可。
public class Client {
public static void main(String[] args) {
ThirdOrderAdapter thirdOrderAdapter = new ThirdOrderAdapter();
Map map = new HashMap();
map.put("orderId","id");
map.put("people","consignee");
map.put("orderTime","createdAt");
map.put("tel","phoneList");
map.put("foodIdList","foodIdList");
ElemeOrder elemeOrder = new ElemeOrder("2021", "123", "123456789", "123", "1,2,3,4");
thirdOrderAdapter.elemeOrder(elemeOrder, map);
}
}
我们发现,在没有修改任何原始代码的情况下就接入了第三方接口,如果再新增第三方接口,我们可以在IThirdOrderService接口中进行添加,当然,也可以新增一个接口,利用Java多实现特性,如下,加入了大众点评。
public class ThirdOrderAdapter extends OrderServiceImpl implements IThirdOrderService , IDaZhongOrderService{
@Override
public String meiTuanOrder(MeituanOrder meituanOrder , Map<String,String> filedMappingList) {
return super.placeAnOrder(converter(meituanOrder,filedMappingList));
}
@Override
public String elemeOrder(ElemeOrder elemeOrder , Map<String,String> filedMappingList) {
return super.placeAnOrder(converter(elemeOrder,filedMappingList));
}
@Override
public String daZhongOrder(DaZhongOrder daZhongOrder, Map<String,String> filedMappingList) {
return super.placeAnOrder(converter(daZhongOrder,filedMappingList));;
}
}
适配器模式的使用场景
1.系统需要使用现在的类,而类的方法不满足或者不符合新需求。
2.对一个类进行扩展
3.想利用类中的某一些方法,但是不去修该原业务逻辑。
适配器模式的优点
1.类能够复用
2.类的扩展性更好
3.可以让多个不想关的类在一起运行。
适配器模式的缺点
1.如果系统中使用的适配器过多,则会提升系统的复杂性,不利于维护。
总结
设计模式可以提高我们系统的复用性,使代码很好的解耦,但是不要为了使用设计模式,或者为了提高代码B格去使用设计模式,因为如果使用不当,将会是一种灾难。
今天的分享就到这里,感谢你的观看,我们下期见。