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

  • 相关阅读:
    Atitit.ati orm的设计and架构总结 适用于java c# php版
    Atitit.ati dwr的原理and设计 attilax 总结 java php 版本
    Atitit.ati dwr的原理and设计 attilax 总结 java php 版本
    Atitit. 软件设计 模式 变量 方法 命名最佳实践 vp820 attilax总结命名表大全
    Atitit. 软件设计 模式 变量 方法 命名最佳实践 vp820 attilax总结命名表大全
    Atitit 插件机制原理与设计微内核 c# java 的实现attilax总结
    Atitit 插件机制原理与设计微内核 c# java 的实现attilax总结
    atitit.基于  Commons CLI 的命令行原理与 开发
    atitit.基于  Commons CLI 的命令行原理与 开发
    atitit.js 与c# java交互html5化的原理与总结.doc
  • 原文地址:https://www.cnblogs.com/mq0036/p/13717861.html
Copyright © 2011-2022 走看看