单一原则
单一职责解释是一个模块只负责完成一个职责或者功能,主要是提升方法的可维护性和复用性。下面看一个例子:
public boolean checkUsernameOrPassword(String str) {return str != null && str.length() > 5 && str.length() < 18;}
public boolean checkPassword(String password) {boolean checkLen = password != null && password.length() > 5 && password.length() < 18;return checkLen && (校验大小写和数字);}public boolean checkUsername(String username) {return userName != null && userName.length() > 5 && userName.length() < 18;}
KISS原则
出处:设计模式之美// 第一种使用正则public boolean isValidIpAddressV1(String ipAddress) {if (StringUtils.isBlank(ipAddress)) return false;String regex = "^(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|[1-9])\\."+ "(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)\\."+ "(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)\\."+ "(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)$";return ipAddress.matches(regex);}// 第二种使用根据规则逐级判断public boolean isValidIpAddressV2(String ipAddress) {if (StringUtils.isBlank(ipAddress)) return false;String[] ipUnits = StringUtils.split(ipAddress, '.');if (ipUnits.length != 4) {return false;}for (int i = 0; i < 4; ++i) {int ipUnitIntValue;try {ipUnitIntValue = Integer.parseInt(ipUnits[i]);} catch (NumberFormatException e) {return false;}if (ipUnitIntValue < 0 || ipUnitIntValue > 255) {return false;}if (i == 0 && ipUnitIntValue == 0) {return false;}}return true;}
出处:知乎《KMP 算法详解》// 暴力搜索int search(String pat, String txt) {int M = pat.length();int N = txt.length();for (int i = 0; i <= N - M; i++) {int j;for (j = 0; j < M; j++) {if (pat[j] != txt[i+j])break;}// pat 全都匹配了if (j == M) return i;}// txt 中不存在 pat 子串return -1;}// 使用KMPpublic class KMP {private int[][] dp;private String pat;public KMP(String pat) {this.pat = pat;int M = pat.length();// dp[状态][字符] = 下个状态dp = new int[M][256];// base casedp[0][pat.charAt(0)] = 1;// 影子状态 X 初始为 0int X = 0;// 构建状态转移图(稍改的更紧凑了)for (int j = 1; j < M; j++) {for (int c = 0; c < 256; c++)dp[j][c] = dp[X][c];dp[j][pat.charAt(j)] = j + 1;// 更新影子状态X = dp[X][pat.charAt(j)];}}public int search(String pat ,String txt) {int M = pat.length();int N = txt.length();// pat 的初始态为 0int j = 0;for (int i = 0; i < N; i++) {// 计算 pat 的下一个状态j = dp[j][txt.charAt(i)];// 到达终止态,返回结果if (j == M) return i - M + 1;}// 没到达终止态,匹配失败return -1;}}
很明显,KMP实现难度、可读性、可维护性都比第一种高。但是如果在特定的场景,比如处理大文本字符串出现性能瓶颈的时候,第二种能比第一种效率高N倍。而解决性能等困难的问题,本来就要用更复杂的方法,所以并不违背Kiss原则。
1.不要过度“炫技”,如果KMP只是用来处理平时工作用到的小字符串,那么也是违背Kiss原则的。
DRY原则
public boolean checkPassword(String password) {return password != null && password.length() > 6 && password.length() < 18;}public boolean checkUsername(String userName) {return userName != null && userName.length() > 6 && userName.length() < 18;}
上面两个方法去校验用户名和密码,它们实现逻辑相同,但是功能语义不一样,如果把它们合到一个方法 “checkUserNameOrPassWord”里面,一个方法做了两件事,违反了单一原则,日后随着业务发展,如果校验密码的规则改变了,那么就会影响到校验用户名逻辑,又需要重新拆分。
// 第一种使用正则public boolean isValidIpAddressV1(String ipAddress) {if (StringUtils.isBlank(ipAddress)) return false;String regex = "^(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|[1-9])\\."+ "(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)\\."+ "(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)\\."+ "(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)$";return ipAddress.matches(regex);}// 第二种使用根据规则逐级判断public boolean isValidIpAddressV2(String ipAddress) {if (StringUtils.isBlank(ipAddress)) return false;String[] ipUnits = StringUtils.split(ipAddress, '.');if (ipUnits.length != 4) {return false;}for (int i = 0; i < 4; ++i) {int ipUnitIntValue;try {ipUnitIntValue = Integer.parseInt(ipUnits[i]);} catch (NumberFormatException e) {return false;}if (ipUnitIntValue < 0 || ipUnitIntValue > 255) {return false;}if (i == 0 && ipUnitIntValue == 0) {return false;}}return true;}
2.增加维护成本:如果到时候需要改变校验IP的逻辑,那么很可能改了一个忘记改了另一个,导致系统出现故障。
public class UserService{private UserManager userManager;public ServiceResult<User> getUserById(long userId){boolean existed = userManager.checkUserExisted(userId);if(!existed){ServiceResult.getFailureResult("code","msg");}User user = userManager.getUserById(userId);return ServiceResult.getSuccessResult(user);}}public static class UserManager {public User getUserById(long userId){boolean existed = userManager.checkUserExisted(userId);if(!existed){// 抛异常}return 查询数据库结果;}public boolean checkUserExisted(long userId){return 根据一定规则校验的结果}}
上面这段代码,分别两次的去调用了 “checkUserExisted”方法,导致了执行重复,违反了DRY原则。一般对于执行重复,可以调整代码顺序,统一规范来避免这个问题。比如说对于业务校验,我们可以放在Manager层统一处理。
命名
1.利用上下文缩减单词,如getUserName,在User类这个上文环境下,可缩减成getName。
2.用缩写来代替,如 to=>2、business=>biz、DailyActiveUse => DAU。
参数定义
当方法参数大于等于 4 个的时候,参数就有点过多了,主要影响到代码的可读性和易用性,我们可以看好的框架源码,如Spring定义方法入参很少超过三个。如果参数过多,一般有两种方法:
2.将多个参数封装为对象。
1.null是否有业务意义。比如说更新表字段时,null代表不更新、查询数据时null代表没有。以上场景用Integer更合适。
2.参数是否必填,如果必填的话,使用int更合适。
方法体
出处:Spring框架源码public static void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {// ...// Register BeanPostProcessorChecker that logs an info message when// a bean is created during BeanPostProcessor instantiation, i.e. when// a bean is not eligible for getting processed by all BeanPostProcessors.int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));// Separate between BeanPostProcessors that implement PriorityOrdered,// Ordered, and the rest.List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<BeanPostProcessor>();List<BeanPostProcessor> internalPostProcessors = new ArrayList<BeanPostProcessor>();List<String> orderedPostProcessorNames = new ArrayList<String>();List<String> nonOrderedPostProcessorNames = new ArrayList<String>();for (String ppName : postProcessorNames) {if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);priorityOrderedPostProcessors.add(pp);if (pp instanceof MergedBeanDefinitionPostProcessor) {internalPostProcessors.add(pp);}}else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {orderedPostProcessorNames.add(ppName);}else {nonOrderedPostProcessorNames.add(ppName);}}// First, register the BeanPostProcessors that implement PriorityOrdered.sortPostProcessors(priorityOrderedPostProcessors, beanFactory);registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);// Next, register the BeanPostProcessors that implement Ordered.List<BeanPostProcessor> orderedPostProcessors = new ArrayList<BeanPostProcessor>();for (String ppName : orderedPostProcessorNames) {BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);orderedPostProcessors.add(pp);if (pp instanceof MergedBeanDefinitionPostProcessor) {internalPostProcessors.add(pp);}}//...}
移除过深的嵌套层次
// 改造前public void example(List<String> strList,String substr) {if(CollectionUtils.isNotEmpty(strList) && StringUtils.isNotBlank(substr)){for (String str :strList) {if(str != null){if(str.contains(substr)) {// 调用存在业务方法}else {// 调用不存在业务方法}}}}}// 改造后public void example(List<String> strList, String substr) {// 利用return提前退出if (CollectionUtils.isEmpty(strList) || StringUtils.isBlank(substr)) {return;}for (String str : strList) {// 利用continue,提前结束本次循环if (str == null) {continue;}if (str.contains(substr)) {// 调用业务方法}else {// 调用不存在业务方法}}}
2.去除多余的if-else
public void example(String str) {if (StringUtils.isBlank(str)) {return;} else { // 此处的else可以去掉// 做其他业务逻辑}}
3.抽出部分嵌套逻辑,封装为方法
注释
1.不要太依赖注释,这会让你有兜底的想法,而忽略代码里的可读性。
推荐阅读
GTS云巧乘风者征文大赛上线!
云巧是“组装式应用”理念的落地,助力大家提升交付速度,提高交付质量,降低用工成本。参加云巧征文大赛,不仅可以让专业导师团评估,更有888元猫超卡和天猫精灵Sound等你来~前100名参加就可获得38元天猫超市卡哦,先到先得!
点击阅读原文查看详情。