代码重构记录
旧代码复用率低,导致重复代码多,耦合严重,往往修改一个参数就需要修改多个接口,一番挣扎之后决定对旧的代码进行重构
先贴出一段旧代码,因为之前忘了在本地编辑器截图,还好在git上能查看历史代码
可以看到框住的这一部分代码,都是一些对参数的校验代码或者是业务的校验代码,这部分的代码不适合放在具体的业务实现类中,这会导致这个方法”责任”过重,也违反了单一职责,针对这一部分的代码,使用了AOP进行处理,将校验(验证)的代码都放在了切面中,解耦的同时也能够更加灵活地进行控制。
// 定义了一个Aspect去实现public class DataValidateAspect extends Result {@Pointcut(value = "execution(...)")public void validatePointCut() {}@Around("validatePointCut()")public Object validateBeforeCheck(ProceedingJoinPoint joinPoint) throws Throwable {MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();String[] paramsName = methodSignature.getParameterNames();// 获取参数下标int request = ArrayUtils.indexOf(paramsName, "xxx");int check = ArrayUtils.indexOf(paramsName, "xxx");Object[] args = joinPoint.getArgs();if (args[request] instanceof UserRequest) {... // 忽略一些代码// 进行一系列的校验if (phoneNumber != null) {phoneNumber = filterPhone(phoneNumber);userRequest.setPhoneNumber(phoneNumber);// 校验手机号if (phoneNumber == null) {log.info("phoneNumber verification failed...");return result(StatusCode.PARAM_WRONG);}}// 校验身份证if (!checkIdCard(idCardNo)) {log.info("idCard verification failed...");return result(StatusCode.PARAM_WRONG);}userName = checkName(userName);// 校验姓名if (userName == null) {log.info("username verification failed...");return result(StatusCode.PARAM_WRONG);}... // 忽略后续的校验代码}return joinPoint.proceed(); //}}
以上就完成了将校验从业务代码中解耦出来,并且改切面还可以配置不同的切点,对不同的实现业务进行校验的步骤。修改校验规则的时候,也只需要对切面的代码进行更改就可以影响到多个实现类。
继续贴出后面的一段代码:

对外暴露多个接口(Controller),但多个接口都调用了同一个Service,通过code不同去实现逻辑,如上图所示,switch…case -> todo 的方式,这样当接口数目增多,分支多了之后,一个方法的代码就是几百行,显然很不合理。因为前面的校验代码已经交给AOP去处理了,后面的逻辑代码可以进一步拆分为多个实现类去完成,这里又因为每个实现有相似的规则,于是想到了使用模板方法(设计模式)去重构。
@Slf4jpublic abstract class AbstractCheckData extends Result {@Autowiredprotected BusinessMapper businessMapper;@Autowiredprotected UserListService userListService;@Autowiredprotected RequestDataMapper requestDataMapper;@Autowiredprotected TransHistoryService transHistoryService;/*** 实现模板(Template method)** @param userRequest* @param checkType* @return*/public final String checkData(UserRequest userRequest, String checkType) {// 1. 查询历史记录实体封装RequestDataHistory requestDataHistory = createDataHistory(userRequest, checkType);// 2. 商户信息查询Business business = getBusiness(userRequest, checkType);// 3. 用户信息查询UserList userList = getUserList(userRequest);// 4. 各项的todoreturn check(userRequest, requestDataHistory, business, userList);}.../*** 交给子类实现的具体方法** @return*/protected abstract String check(UserRequest userRequest, RequestDataHistory requestDataHistory, Business findBusiness, UserList userList);}// 子类@Servicepublic class CheckMultiImpl extends AbstractCheckData {@Overrideprotected String check(UserRequest userRequest, RequestDataHistory requestDataHistory, Business findBusiness, UserList userList) {... // 具体逻辑实现}}...
// 接口public interface CheckDataService {/*** 相关信息查询,根据 checkType 判断** @param userRequest* @param checkType* @return*/String checkData(UserRequest userRequest, String checkType);}@Servicepublic class CheckDataServiceImpl<R> implements CheckDataService {@Overridepublic String checkData(UserRequest userRequest, String checkType) {// 表驱动代替if + 多态实现Map<String, AbstractCheckData> checkDataMap = getCheckMap();AbstractCheckData checkSub = checkDataMap.get(checkType);return checkSub.checkData(userRequest, checkType);}/*** 映射表* checkType -> impl** @return*/private Map<String, AbstractCheckData> getCheckMap() {Map<String, AbstractCheckData> map = new HashMap<>();map.put("1001", checkMulti);map.put("1002", checkBlack);map.put("1003", checkGray);map.put("1004", checkActivity);map.put("1005", checkRepaymentTime);map.put("1006", checkRepaymentAmount);map.put("1007", checkRepaymentIntensity);return map;}}
重构之后,使用了模板方法模式,让步骤成为一个模板,定义一个抽象的父类,将公共的模板方法在父类中进行实现,代码清爽了不少,也更容易拓展,修改,可读性也提高了。缺点是类文件会增加(一个接口独立一个实现类)。
