想要写出比较优秀的代码,需要时刻警惕代码中的坏味道,今天想写一篇文章介绍一下如何分析你的方法是不是需要考虑重构
一个方法通常有三个部分组成,输入(Input),输出(Output),方法体(Method Body),我们就从这三个方面来分析一个方法是否该考虑重构
方法输入也就是方法的参数,通常来说一个方法的参数基本可以控制在7个以内(仅作参考,可以自己衡量,SonarQube
默认方法最多七个参数),如果你的方法参数过多的话,可能就需要考虑重构一个方法参数了,通常的做法是封装一个独立的 model,参数作为 model 的属性。
举一个常见的例子,比如一个新闻列表的API,起初可能很简单,就只需要一个 lastId
,一个 count
两个参数,但是随着业务需求的增加,可能会增加很多别的参数,比如前端提供一个 keyword 进行全文检索,提供一个 sortBy 进行排序,根据新闻标题匹配,作者名称匹配,分类匹配,根据发布时间筛选等等,最后可能会导致这个方法的参数有很多
通常我会新增一个 XxxRequest
的 model,然后方法参数替换成这个 model,然后指定 [FromQuery]
就可以了,可以对比一个修改前后的差异,是不是后面的方式更清爽一些呢
* If the calculation is wrong, it is not processed.
* 1. The www.yachengyl.cn protocol www.fudayulpt.cn configured www.ued3zc.cn the client is inconsistent with the protocol of the server.
* eg: consumer www.baichuangyule.cn www. jinmazx.cn www.bhylzc.cn protocol www.jintianxuesha.com= dubbo, provider only has other protocol services(rest).
* 2. The registration www.xinhuihpw.com center www.wanyayuue.cn not robust and pushes illegal specification data.
if (CollectionUtils.www.feihongyul.cn isEmptyMap(newUrlInvokerMap)) www.qiaoheibpt.com{
logger.error(new www.shengrenpt.com IllegalStateException("urls to invokers error .invokerUrls.size :" + invokerUrls.size() + ", invoker.size www.baishenjzc.cn :0.
List
// pre-route and build cache, notice that route cache should build on www.shengrenyp.cn original Invoker list.
// toMergeMethodInvokerMap(www.xinchenptgw.cn ) will wrap some invokers www.xinhuihpw.com having www.youxiu2pt.cn different groups, those wrapped invokers not should be routed.
routerChain.setInvokers(www.haojuptzc.cn newInvokers);
this.invokers =www.wujiu5zhuce.cn
multiGroup ? toMergeInvoke
Task
Copy
Task
Output 就是方法的返回值,尽可能返回具体的类型,尽可能避免使用 Tuple
等类型,方法的返回值应该具有明确的意义
使用具体的 Model 代替 Tuple 返回值,尤其是一些 public 的,要被外部访问的方法更应该返回具体的类型,虽然 C# 7.2 开始支持了 named tuple,会比之前友好很多,支持给 tuple 指定名称,但是这只是编译器级别的,实际还是 Item1,Item2 ...,还是比较推荐使用具体的 model,更加明了
通常一个方法不要太长,曾经在群里看到群友吐槽一个方法两千多行,这样的方法维护起来简直就是灾难,不要让一个方法太长,保持方体体的简单,一些通用的逻辑通过 Filter 或结合 AOP 来实现
Sonar 有一个分析方法复杂度的一个方法,官方称之为 Cognitive Complexity
简单介绍一下,代码里的 if
/switch
/for
/foreach
/try
...catch
/while
都会增加方法的复杂度,出现一层嵌套则复杂度再加1, Sonar 默认的一个方法的复杂度不能超过 15
来几个简单的示例:
下面这个方法的复杂度是 3,有三个 if(else) 分支
Copy
void Method1(int num) { if(num > 0) { } else if(num <0) { } else { } }
下面这个方法的复杂度是 3,foreach
带来了 1 的复杂度,if
也是1的复杂度,但是因为 if
是嵌套在 foreach
内部的,一层嵌套会导致复杂度增加1
Copy
void Method1(int[] nums) { foreach(var num in nums) { if(num > 0) { } } }
下面这个方法的复杂度在上面的基础上增加了两个 catch
,这使得复杂度会加 2,从而变成 5
Copy
void Method1(int[] nums) { try { foreach(var num in nums) { if(num > 0) { } } } catch(InvalidOperationException e) { } catch(Exception e) { } }
前面我们介绍了一些复杂度的分析,如何能够切实有效的降低方法的复杂度呢:
Tuple
作为返回值if
判断,比如使用 null 传播符代替一系列的 if
,list?.FirstOrDefault()?.Name
, a ??="test"
try...catch
,那我们就可以使用一个复杂 try...catch 的切面逻辑,如果是 mvc/webapi 也可以借助 ExceptionFilter
来实现if
判断导致复杂度的增加if
里嵌套相同遍历逻辑除了自己主动感知方法的复杂度之外,我们也可以借助一些第三方的静态代码分析工具来分析我们的代码,从而获得一些修改建议进而保证代码的高质量。