代码重构记录
旧代码复用率低,导致重复代码多,耦合严重,往往修改一个参数就需要修改多个接口,一番挣扎之后决定对旧的代码进行重构
先贴出一段旧代码,因为之前忘了在本地编辑器截图,还好在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去处理了,后面的逻辑代码可以进一步拆分为多个实现类去完成,这里又因为每个实现有相似的规则,于是想到了使用模板方法(设计模式)去重构。
@Slf4j
public abstract class AbstractCheckData extends Result {
@Autowired
protected BusinessMapper businessMapper;
@Autowired
protected UserListService userListService;
@Autowired
protected RequestDataMapper requestDataMapper;
@Autowired
protected 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. 各项的todo
return check(userRequest, requestDataHistory, business, userList);
}
...
/**
* 交给子类实现的具体方法
*
* @return
*/
protected abstract String check(UserRequest userRequest, RequestDataHistory requestDataHistory, Business findBusiness, UserList userList);
}
// 子类
@Service
public class CheckMultiImpl extends AbstractCheckData {
@Override
protected 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);
}
@Service
public class CheckDataServiceImpl<R> implements CheckDataService {
@Override
public 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;
}
}
重构之后,使用了模板方法模式,让步骤成为一个模板,定义一个抽象的父类,将公共的模板方法在父类中进行实现,代码清爽了不少,也更容易拓展,修改,可读性也提高了。缺点是类文件会增加(一个接口独立一个实现类)。