項目背景
最近公司需要引入一款新的人臉設(shè)備腿倚,并且想要整合一下之前的設(shè)備敷燎,想要做到一個項目里面能使用兩個不同平臺的設(shè)備硬贯,這兩個平臺之前都做過對接饭豹,但是由于寫的人不同拄衰,所以邏輯上有一點點不一樣翘悉,但是其實也可以按照同樣的邏輯去寫妖混。
重構(gòu)第一步:抽取共性與差異
注意,重構(gòu)一定要在你熟悉業(yè)務(wù)與架構(gòu)的情況下進行重構(gòu)诗越,如果不熟悉嚷狞,并且沒有必要的情況下床未,不建議對代碼做任何修改振坚,經(jīng)過上線校驗的代碼渡八,寫的再差也比沒經(jīng)過驗證的代碼強。
以我這次重構(gòu)的一個步驟為例宏娄,人員信息同步(增加孵坚、修改),其實他就可以抽象成以下這么一個流程
獲取在線的設(shè)備 -> 檢查圖片質(zhì)量是否合格 -> 開啟線程異步同步 -> 檢查設(shè)備是否存在該人員 卖宠?修改 : 刪除
重構(gòu)第二步:根據(jù)第一步的流程確定架構(gòu)
這里先放一下原代碼扛伍,這段代碼在只有一種設(shè)備的情況下是沒有問題的蜒秤,但是現(xiàn)在我這里一個程序需要兼容不同的設(shè)備,并且設(shè)備的處理邏輯大體上是相同的帅刊,只不過與設(shè)備交互的地方有點不一樣
private void doSyncPerson(PersonalInfo personalInfo, OperateType operateType, UserSyncReq userInfo, String address) {
Integer deviceId;
try {
deviceId = deviceService.getDeviceId(address);
} catch (Exception e) {
throw new ServiceException("獲取設(shè)備id失敗,address: " + address);
}
if (isExistsPerson(address, deviceId, String.valueOf(userInfo.getUser_id()))) {
operateType = OperateType.EditPerson;
personalInfo.setOperator(operateType.name());
}
personalInfo.getInfo().setDeviceId(deviceId);
HaiqingResp response = haiqingDeviceClient.post(address, operateType, personalInfo,HaiqingResp.class);
if(response.isError()){
throw new ServiceException("同步人員信息失敗");
}
}
private PersonalInfo searchPerson(String address, int deviceId, String searchId) {
Map<String, Object> json = new HashMap<>();
json.put("operator", OperateType.SearchPerson.name());
Map<String, Object> info = new HashMap();
info.put("DeviceID", deviceId);
info.put("SearchType", 0);
info.put("SearchID", searchId);
info.put("Picture", 0);
json.put("info", info);
return haiqingDeviceClient.post(address, OperateType.SearchPerson, json,PersonalInfo.class);
}
private boolean isExistsPerson(String address, int deviceId, String searchId) {
PersonalInfo personalInfo = this.searchPerson(address, deviceId, searchId);
if (personalInfo == null) {
return false;
}
if (personalInfo.getInfo() == null) {
return false;
}
return personalInfo.getInfo().getCustomizeId() != null;
}
開始重構(gòu)
@Override
public AcmsRespDTO userSync(UserSyncReq userSyncReq) {
List<String> device_address_list = userSyncReq.getDevice_address_list();
userSyncReq.setDevice_address_list(filterOnlineDevices(device_address_list));
// checkImage是個接口方法 由子類實現(xiàn)
boolean isPass = checkImage(userSyncReq.getPhoto(), userSyncReq.getDevice_address_list());
if(!isPass){
return AcmsRespDTO.error("圖片校驗失敗");
}
for (String address : userSyncReq.getDevice_address_list()) {
ScheduleConfig scheduleConfig = new ScheduleConfig();
scheduleConfig.setKey(IdUtil.fastSimpleUUID());
scheduleConfig.setStartTime(new Date());
scheduleConfig.setDelay(defaultDelay);
// 用戶同步的抽象類
AbstractUserSyncThread userSyncThread = getUserSyncThread();
userSyncThread.setScheduleConfig(scheduleConfig);
userSyncThread.setUserSyncReq(userSyncReq);
userSyncThread.setAddress(address);
scheduleConfig.setRunnable(userSyncThread);
scheduledTaskService.addPollingTask(scheduleConfig);
}
return AcmsRespDTO.success();
}
public abstract class AbstractUserSyncThread extends AbstractScheduleTimesRunnable {
protected UserSyncReq userSyncReq;
protected String address;
@Override
protected void timesRun() {
if(checkUserExist()){
log.info("用戶存在,修改");
updateUser();
}else{
log.info("用戶不存在吧兔,新增");
addUser();
}
}
/**
* 檢查用戶是否存在 子類實現(xiàn)
* @return
*/
protected abstract boolean checkUserExist();
//子類實現(xiàn)新增方法
protected abstract void addUser();
//子類實現(xiàn)修改方法
protected abstract void updateUser();
public UserSyncReq getUserSyncReq() {
return userSyncReq;
}
public void setUserSyncReq(UserSyncReq userSyncReq) {
this.userSyncReq = userSyncReq;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
第三步:由子類確認(rèn)具體實現(xiàn)
這里以其中一個設(shè)備為例
@Service("HaiQing")
@Slf4j
public class HaiQingAcmsInterface extends AbstractAcmsInterface {
@Autowired
private HaiqingDeviceClient haiqingDeviceClient;
@Override
public AcmsRespDTO usePicCheck(String photo) {
return null;
}
@Override
public boolean checkImagePass(String photo, String address) {
DetectFaceFromPicReq request = new DetectFaceFromPicReq();
request.setOperator(OperateType.DetectFaceFromPic);
DetectFaceFromPicReq.Info info = new DetectFaceFromPicReq.Info();
info.setPicinfo(photo);
request.setInfo(info);
try {
DetectFaceFromPicResp detectFaceFromPicResp = haiqingDeviceClient
.post(address, OperateType.DetectFaceFromPic, request, DetectFaceFromPicResp.class);
int detectFace = Optional.ofNullable(detectFaceFromPicResp)
.map(DetectFaceFromPicResp::getInfo)
.map(DetectFaceFromPicResp.Info::getDetectFace)
.orElse(0);
return detectFace == 1;
} catch (Exception ignored) {
log.error("校驗圖片異常",ignored);
}
return false;
}
@Override
public AbstractUserSyncThread getUserSyncThread() {
return new HaiQingUserSyncThread();
}
@Override
public AbstractUserDelThread getUserDelThread() {
return new HaiQingUserDelThread();
}
@Override
public AbstractDeviceSyncThread getDeviceSyncThread() {
return new HaiQingDeviceSyncThread();
}
}
public class HaiQingUserSyncThread extends AbstractUserSyncThread {
private static HaiqingDeviceClient haiqingDeviceClient = SpringUtils.getBeanByClass(HaiqingDeviceClient.class);
private static HaiQingDeviceService haiQingDeviceService = SpringUtils.getBeanByClass(HaiQingDeviceService.class);
@Override
protected boolean checkUserExist() {
return haiQingDeviceService.checkUserExist(address,userSyncReq.getUser_id());
}
@Override
protected void addUser() {
PersonalInfo personalInfo = getPersonalInfo();
personalInfo.setOperator(OperateType.AddPerson.name());
HaiqingResp response = haiqingDeviceClient.post(address, OperateType.AddPerson, personalInfo,HaiqingResp.class);
if(response.isOk()){
stop();
}
}
@Override
protected void updateUser() {
PersonalInfo personalInfo = getPersonalInfo();
personalInfo.setOperator(OperateType.EditPerson.name());
HaiqingResp response = haiqingDeviceClient.post(address, OperateType.EditPerson, personalInfo,HaiqingResp.class);
if(response.isOk()){
stop();
}
}
private PersonalInfo getPersonalInfo(){
PersonalInfo personalInfo = new PersonalInfo();
personalInfo.setPicInfo(userSyncReq.getPhoto());
Personal personal = new Personal();
personal.setIdType(0);
personal.setCustomizeId(Long.parseLong(userSyncReq.getUser_id()));
personal.setTelnum(userSyncReq.getPhone());
personal.setName(userSyncReq.getUser_name());
personalInfo.setInfo(personal);
Integer deviceId = haiQingDeviceService.getDeviceId(address);
personal.setDeviceId(deviceId);
return personalInfo;
}
}
總結(jié)
公司內(nèi)部的代碼也就只能放部分片段了,重構(gòu)以后代碼相對來說更加清晰了一些吴藻,
說說重構(gòu)的好處和壞處弓柱,按照這種架構(gòu)的話呢矢空,它固定了程序的流程妇多,讓程序以一定的流程去執(zhí)行伤哺,但是也減少的一定的自由性,如果有其他設(shè)備要接入的話者祖,按照這套模式就非常簡單(按照這個架構(gòu)重構(gòu)另一個設(shè)備的代碼就只花了一個小時立莉,在對應(yīng)實現(xiàn)的地方把原來的代碼貼上去就行),但是如果你要改架構(gòu)七问,就會變得非常復(fù)雜蜓耻,還有,這種架構(gòu)也用到了模板設(shè)計模式械巡、工廠設(shè)計模式刹淌、對于新人來說可能會難以理解。