单一原则
单一职责解释是一个模块只负责完成一个职责或者功能,主要是提升方法的可维护性和复用性。下面看一个例子:
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;
}
// 使用KMP
public 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 case
dp[0][pat.charAt(0)] = 1;
// 影子状态 X 初始为 0
int 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 的初始态为 0
int 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元天猫超市卡哦,先到先得!
点击阅读原文查看详情。