代碼重構之法——方法重構分析
Intro
想要寫出比較優秀的代碼,需要時刻警惕代碼中的壞味道,今天想寫一篇文章介紹一下如何分析你的方法是不是需要考慮重構
一個方法通常有三個部分組成,輸入(Input),輸出(Output),方法體(Method Body),我們就從這三個方面來分析一個方法是否該考慮重構
Input
方法輸入也就是方法的引數,通常來說一個方法的引數基本可以控制在7個以內(僅作參考,可以自己衡量,SonarQube 默認方法最多七個引數),如果你的方法引數過多的話,可能就需要考慮重構一個方法引數了,通常的做法是封裝一個獨立的 model,引數作為 model 的屬性,
舉一個常見的例子,比如一個新聞串列的API,起初可能很簡單,就只需要一個 lastId,一個 count 兩個引數,但是隨著業務需求的增加,可能會增加很多別的引數,比如前端提供一個 keyword 進行全文檢索,提供一個 sortBy 進行排序,根據新聞標題匹配,作者名稱匹配,分類匹配,根據發布時間篩選等等,最后可能會導致這個方法的引數有很多
通常我會新增一個 XxxRequest 的 model,然后方法引數替換成這個 model,然后指定 [FromQuery] 就可以了,可以對比一個修改前后的差異,是不是后面的方式更清爽一些呢
Task<IActionResult> List(int lastId, int count, string title, string author, string keyword, int categoryId, string sortBy, DateTime? beginTime, DateTime? endTime)
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) 分支
void Method1(int num)
{
if(num > 0)
{
}
else if(num <0)
{
}
else
{
}
}
下面這個方法的復雜度是 3,foreach 帶來了 1 的復雜度,if 也是1的復雜度,但是因為 if 是嵌套在 foreach 內部的,一層嵌套會導致復雜度增加1
void Method1(int[] nums)
{
foreach(var num in nums)
{
if(num > 0)
{
}
}
}
下面這個方法的復雜度在上面的基礎上增加了兩個 catch,這使得復雜度會加 2,從而變成 5
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
前面我們介紹了一些復雜度的分析,如何能夠切實有效的降低方法的復雜度呢:
- 方法引數不宜過多,引數過多考慮重構輸入引數,通常可以新建一個 model 來管理輸入引數
- 方法回傳值不宜使用意義不明的回傳值,盡量不用
Tuple作為回傳值 - 方法的行數不要太多,利用新語法減少行數,減少
if判斷,比如使用 null 傳播符代替一系列的if,list?.FirstOrDefault()?.Name,a ??="test" - 多個方法的相同邏輯使用切面邏輯處理,比如每個方法里都有
try...catch,那我們就可以使用一個復雜 try...catch 的切面邏輯,如果是 mvc/webapi 也可以借助ExceptionFilter來實作 - 引數校驗使用微軟的 ModelValidator 或者使用 FluentValidation 進行校驗,在代碼里盡量避免使用大量的
if判斷導致復雜度的增加 - 仔細 review 代碼,有些邏輯是否合并在一起,避免在多個
if里嵌套相同遍歷邏輯 - and more...(等你來補充
More
除了自己主動感知方法的復雜度之外,我們也可以借助一些第三方的靜態代碼分析工具來分析我們的代碼,從而獲得一些修改建議進而保證代碼的高質量,
SonarQube 是目前使用較多的工具,可以方便的和 CI 集成,有一個 SonarCloud 網站提供云服務,可以輕松為你的開源專案集成靜態代碼分析,有興趣可以看看,地址是 https://sonarcloud.io/,之前還用過 codacy,似乎不太流行,推薦 SonarQube
Reference
- https://sonarcloud.io/
- https://www.sonarsource.com/docs/CognitiveComplexity.pdf
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/38.html
標籤:C#
