zoukankan      html  css  js  c++  java
  • sonar扫描——方法重构分析

    代码重构之法——方法重构分析

    Intro#

    想要写出比较优秀的代码,需要时刻警惕代码中的坏味道,今天想写一篇文章介绍一下如何分析你的方法是不是需要考虑重构

    一个方法通常有三个部分组成,输入(Input),输出(Output),方法体(Method Body),我们就从这三个方面来分析一个方法是否该考虑重构

    Input#

    方法输入也就是方法的参数,通常来说一个方法的参数基本可以控制在7个以内(仅作参考,可以自己衡量,SonarQube 默认方法最多七个参数),如果你的方法参数过多的话,可能就需要考虑重构一个方法参数了,通常的做法是封装一个独立的 model,参数作为 model 的属性。

    举一个常见的例子,比如一个新闻列表的API,起初可能很简单,就只需要一个 lastId,一个 count 两个参数,但是随着业务需求的增加,可能会增加很多别的参数,比如前端提供一个 keyword 进行全文检索,提供一个 sortBy 进行排序,根据新闻标题匹配,作者名称匹配,分类匹配,根据发布时间筛选等等,最后可能会导致这个方法的参数有很多

    通常我会新增一个 XxxRequest 的 model,然后方法参数替换成这个 model,然后指定 [FromQuery] 就可以了,可以对比一个修改前后的差异,是不是后面的方式更清爽一些呢

    Copy
    Task<IActionResult> List(int lastId, int count, string title, string author, string keyword, int categoryId, string sortBy, DateTime? beginTime, DateTime? endTime)
    
    Copy
    Task<IActionResult> List([FromQuery]NewsListQueryRequest request)
    

    Output#

    Output 就是方法的返回值,尽可能返回具体的类型,尽可能避免使用 Tuple 等类型,方法的返回值应该具有明确的意义

    使用具体的 Model 代替 Tuple 返回值,尤其是一些 public 的,要被外部访问的方法更应该返回具体的类型,虽然 C# 7.2 开始支持了 named tuple,会比之前友好很多,支持给 tuple 指定名称,但是这只是编译器级别的,实际还是 Item1,Item2 ...,还是比较推荐使用具体的 model,更加明了

    Body#

    通常一个方法不要太长,曾经在群里看到群友吐槽一个方法两千多行,这样的方法维护起来简直就是灾难,不要让一个方法太长,保持方体体的简单,一些通用的逻辑通过 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)
      {
      }
    }
    

    更多示例可以参考官方介绍: https://www.sonarsource.com/docs/CognitiveComplexity.pdf

    Reduce Complexity#

    前面我们介绍了一些复杂度的分析,如何能够切实有效的降低方法的复杂度呢:

    1. 方法参数不宜过多,参数过多考虑重构输入参数,通常可以新建一个 model 来管理输入参数
    2. 方法返回值不宜使用意义不明的返回值,尽量不用 Tuple 作为返回值
    3. 方法的行数不要太多,利用新语法减少行数,减少 if 判断,比如使用 null 传播符代替一系列的 iflist?.FirstOrDefault()?.Name, a ??="test"
    4. 多个方法的相同逻辑使用切面逻辑处理,比如每个方法里都有 try...catch,那我们就可以使用一个复杂 try...catch 的切面逻辑,如果是 mvc/webapi 也可以借助 ExceptionFilter 来实现
    5. 参数校验使用微软的 ModelValidator 或者使用 FluentValidation 进行校验,在代码里尽量避免使用大量的 if 判断导致复杂度的增加
    6. 仔细 review 代码,有些逻辑是否合并在一起,避免在多个 if 里嵌套相同遍历逻辑
    7. and more...(等你来补充

    More#

    除了自己主动感知方法的复杂度之外,我们也可以借助一些第三方的静态代码分析工具来分析我们的代码,从而获得一些修改建议进而保证代码的高质量。

    SonarQube 是目前使用较多的工具,可以方便的和 CI 集成,有一个 SonarCloud 网站提供云服务,可以轻松为你的开源项目集成静态代码分析,有兴趣可以看看,地址是 https://sonarcloud.io/,之前还用过 codacy,似乎不太流行,推荐 SonarQube

    Reference#

    ==

    出处:https://www.cnblogs.com/weihanli/p/13636616.html

  • 相关阅读:
    Swift入坑系列—集合类型
    Java正则表达式入门
    Swift基础--手势识别(双击、捏、旋转、拖动、划动、长按)
    Swift之手势总结
    Matlab图像彩色转灰色
    HDU1754_I Hate It(线段树/单点更新)
    Cocos2d-x-lua游戏两个场景互相切换MainScene01切换到MainScene02
    freemarker自己定义标签(二)
    hibernate 在tomcat7.X 下配置mysql数据源
    WinCE隐藏显示任务栏,当任务栏隐藏时将其显示,当任务栏显示时将其隐藏(FindWindow,ShowWindow,IsWindowVisible),
  • 原文地址:https://www.cnblogs.com/mq0036/p/13717861.html
Copyright © 2011-2022 走看看