适配器模式
概述
在日常生活中,我们经常会用到适配器,比如我们家里的插座电压都是220V,而笔记本的额定工作电压是15V,我们怎么让笔记本在正常的插座上工作呢?其实充电器就是一个适配器,他将220V的电压转换成了15V的电压,供电脑工作。
而在开发过程中,我们也经常会遇到这种问题,在学习适配器模式之前,小黄确实做法很low,小黄的工作经常对接不同平台的设备,比如一个车牌号,A厂商通过MQ发过来的字段名是vehicleNumber,B厂商字段名是vehicleNum,C厂商可能又是另外的名字。每每在解析数据的时候,都会让小黄异常头疼。咱们这就使用适配器模式,来统一解析数据
示例程序
对象适配
如上述例子所示,先模拟mq发过来的信息,两个不同设备厂商的发过来的字段是不一样的。
@Data
@NoArgsConstructor
@AllArgsConstructor
public class HKVehicleMq {
//车辆id
private String id;
//车牌号码
private String vehicleNum;
//设备编号
private String deviceId;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class RMVecicleMq {
//车辆id
private String vehicleId;
//车牌号码
private String vehicleNumber;
//设备编号
private String deviceNumber;
}
首先需要一个通用类,来接收数据
@Data
@NoArgsConstructor
@AllArgsConstructor
public class VehicleInfo {
//车辆id
private String id;
//车牌号码
private String vehicleNumber;
//设备编号
private String deviceId;
}
MQAdapter是最重要的适配器,将不同的方法名设置为相同的
public class MQAdapter {
/**
*
* @param strJson
* @param link key为VehicleInfo字段,value为接收字段
* @return
* @throws JsonProcessingException
*/
public static VehicleInfo filter(String strJson, Map<String,String> link) throws JsonProcessingException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
ObjectMapper mapper = new ObjectMapper();
return filter(mapper.readValue(strJson,Map.class),link);
}
public static VehicleInfo filter(Map obj,Map<String,String> link) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
VehicleInfo vehicleInfo = new VehicleInfo();
for (String key : link.keySet()){
Object value = obj.get(link.get(key));
VehicleInfo.class.getMethod("set" + key.substring(0,1).toUpperCase() + key.substring(1),String.class).invoke(vehicleInfo,value);
}
return vehicleInfo;
}
}
测试
@SpringBootTest
class Practice901ApplicationTests {
@Test
void contextLoads() throws JsonProcessingException, InvocationTargetException, NoSuchMethodException, IllegalAccessException {
HKVehicleMq hkVehicleMq = new HKVehicleMq("10001", "浙B12345", "HK10001");
Map<String, String> HKMap = new HashMap<>(3);
HKMap.put("id","id");
HKMap.put("vehicleNumber","vehicleNum");
HKMap.put("deviceId","deviceId");
ObjectMapper mapper = new ObjectMapper();
VehicleInfo HKVehicle = MQAdapter.filter(mapper.writeValueAsString(hkVehicleMq), HKMap);
System.out.println("适配前" + hkVehicleMq);
System.out.println("适配后" + HKVehicle);
RMVecicleMq rmVecicleMq = new RMVecicleMq("10002", "浙B54321", "RM10002");
Map<String, String> RMMap = new HashMap<>(3);
RMMap.put("id","vehicleId");
RMMap.put("vehicleNumber","vehicleNumber");
RMMap.put("deviceId","deviceNumber");
VehicleInfo RMVehicle = MQAdapter.filter(mapper.writeValueAsString(rmVecicleMq), RMMap);
System.out.println("适配前" + rmVecicleMq);
System.out.println("适配后" + RMVehicle);
}
}
//结果
//适配前HKVehicleMq(id=10001, vehicleNum=浙B12345, deviceId=HK10001)
//适配后VehicleInfo(id=10001, vehicleNumber=浙B12345, deviceId=HK10001)
//适配前RMVecicleMq(vehicleId=10002, vehicleNumber=浙B54321, deviceNumber=RM10002)
//适配后VehicleInfo(id=10002, vehicleNumber=浙B54321, deviceId=RM10002)
接口适配
现在我们有一个方法,需要判断车是不是新车,其实在开发过程中,我们只需要将两者改统一即可。但往往事与愿违,尤其是在后期维护过程中,尽量不要去动以前的代码模块
@Slf4j
public class HKVehicleService {
/**
* 判断是否为新车
* @param id 车辆id
* @return 1:新车,0:旧车
*/
public int isNewCar(String id){
log.info("海康车辆:{}",id);
return 1;
}
}
@Slf4j
public class RMVehicleService {
/**
* 判断是否为新车
* @param vehicleId 车辆id
* @return true:新车,false:旧车
*/
public boolean newCar(String vehicleId){
log.info("锐明车辆:{}",vehicleId);
return true;
}
}
首先实现一个通用接口
public interface VehicleAdapterService {
boolean isNewCar(String id);
}
创建不同厂商的不同处理类,来实现该接口
public class HKVehicleAdapterService implements VehicleAdapterService {
private HKVehicleService hkVehicleService = new HKVehicleService();
@Override
public boolean isNewCar(String id) {
int newCar = hkVehicleService.isNewCar(id);
if (newCar == 1) return true;
else return false;
}
}
public class RMVehicleAdapterService implements VehicleAdapterService {
private RMVehicleService rmVehicleService = new RMVehicleService();
@Override
public boolean isNewCar(String id) {
return rmVehicleService.newCar(id);
}
}
测试
@Test
void test01(){
VehicleAdapterService hkVehicleAdapterService = new HKVehicleAdapterService();
boolean HKNewCar = hkVehicleAdapterService.isNewCar("10001");
System.out.println("HK判断新车:" + HKNewCar); //HK判断新车:true
VehicleAdapterService rmVehicle = new RMVehicleAdapterService();
boolean newCar = rmVehicle.isNewCar("10002");
System.out.println("RM判断新车:" + HKNewCar); //RM判断新车:true
}
总结
以上,我们就通过实际案例,学习了适配器模式,并且小黄会逐步的应用在实际开发过程中。
总的来说,适配器模式(有时候也称包装样式或者包装)将一个类的接口适配成用户所期待的。一个适配允许通常因为接口不兼容而不能在一起工作的类工作在一起,做法是将类自己的接口包裹在一个已存在的类中。