[鐵人賽Day30] ASP.Net Core MVC 進化之路 - 工具篇(4) / Refactor with ReSharper

本文將介紹.Net界堪稱神級的工具 - Resharper。

先強調這絕對不是業配文!

筆者也沒有收取廠商任何廣告費用,

只是希望讓更多人認識這套工具。

Resharper

ReSharperJetBrains公司發行的一套Visual Studio擴充套件,

提供的擴充功能很多,

以下僅摘述筆者比較常用的功能介紹:

  • 原始碼查看
  • 程式碼靜態分析
  • 快速重構

 

Price

單純購買擴充套件的話一年需299美元(約9200$ NT),

授權計價皆以為單位,

但續約的價格會相對便宜許多(計價方案如下圖 - 2018.11.14)。

沒使用過的朋友可以到官網下載30天的試用版,

下載之後進行安裝即可(需先安裝Visual Studio 2017)。

 

原始碼查看

雖然現在有非常多的套件都是Open Source

但看個程式碼還要大費周章跑到GitHub總是為令人感到麻煩,

ReSharper整合了自己家的DotPeek(一套免費.Net原始碼查看工具),

讓原始碼查看(快捷鍵F12)功能變的相當輕易,

但真的去用的人還是占少數。

你也許會想:

「Code都寫不完了,哪有時間看原始碼?」

「沒事看原始碼,吃飽太閒嗎?」

「功能好好的,開發也沒問題,幹嘛看?」

 

那換個情境思考,

假設你帶你的小孩看完醫生後去領藥,

你不會關注藥物過敏或副作用嗎?

同樣的場景,換成了你的程式碼,

我們卻只求「會動就好」。

 

我們太習慣「從結果推斷過程」的學習方式,

久了就容易不小心地把錯誤的認知當成「常識」。

 

舉簡單的資料結構而言,

請問以下的程式碼誰執行會比較快?

List<string> stringList = GetStringList();
stringList.LastOrDefault();

string[] stringArray = GetStringArray();
stringArray.LastOrDefault();

 

以直覺的思考方式來說,

陣列是以索引值取得集合中的元素,

一般串列則只會記得下一個元素的記憶體位置。

所以你就會很自然的推想:

  • 陣列是用Array[陣列長度-1]的方式取得最後一筆元素的值,
  • 串列則會逐筆尋找,直到沒有下一個元素為止。

再加上使用了最熟也最不熟的LINQ

 

就會很自然的認為兩者的效能差距應該會非常大(直接找跟逐筆找的差別)。

但真的是這樣嗎?

我們使用原始碼查看功能來看一下LastOrDefault這段程式碼。

public static TSource LastOrDefault<TSource>(this IEnumerable<TSource> source) {
    if (source == null) throw Error.ArgumentNull("source");
    IList<TSource> list = source as IList<TSource>;
    if (list != null) {
        int count = list.Count;
        if (count > 0) return list[count - 1];
    }
    else {
        using (IEnumerator<TSource> e = source.GetEnumerator()) {
            if (e.MoveNext()) {
                TSource result;
                do {
                    result = e.Current;
                } while (e.MoveNext());
                return result;
            }
        }
    }
    return default(TSource);
}

 

程式內容翻譯概述如下:

LastOrDefault是針對 IEnumerable<TSource> 做擴充,

當被呼叫時會先嘗試轉型為 IList<TSource> 的型別,

如果轉型成功就使用[長度 - 1]的方式回傳元素值,

不能轉型的話才會逐筆呼叫 MoveNext() 。

 

當然,我們不可能有時間把每個API看過一遍,

但在使用API得到非預期的結果時,

查看原始碼總比你瞎猜來的好。

 

程式碼靜態分析

這項功能會自動分析我們現有的程式碼,

然後建議我們比較好的寫法。

舉個簡單的範例Demo,

假設我的原始碼長這樣:

var arr = new int[]{ 3, 4, 5, 6, 7 };
var sum = 0;
for (int i=0; i< arr.Length; i++)
{
    sum += arr[i];
}

 

這時ReSharper會很貼心的提醒你。

除了提醒之外,

還可以快速幫你重構。

選擇轉換成LINQ寫法,

一秒幫你轉過去。

按按快捷鍵事情就做完了,

而且還比較好看!

有關程式碼分析的部分有興趣的讀者可以參考官網的介紹。

 

快速重構

接下來要介紹的是ReSharper的靈魂所在 - Refactor(重構)

Martin Fowler大師對重構的定義:

在不改變軟體外部行為的前提下,改變其內部結構,使其更容易理解且易於修改

重構牽涉的範圍之廣,

推薦大家可以閱讀聖經本重構─改善既有程式的設計

此書介紹了許多重構的技巧與名詞,

搭配ReSharper的Refactor使用會更有感覺!

 

下面會有一堆快捷鍵,

真的記不住至少要記住快捷鍵Ctrl+Shift+R- 彈出Refactor的小視窗。

 

一樣開始之前先來看看我寫的爛Code,

這邊我用了一個完全沒意義的tempVariable來存數字,

List<int> list = new List<int>();
int tempVariable = 10;
list.Add(tempVariable);

 

Inline

快捷鍵Ctrl+Alt+N

可以幫我們將變數內容填到傳入的方法中,

按下確定之後它就幫我們做完了。

在整理程式碼的過程,

如果我覺得我取的變數好像沒什麼意義,

我就會試著inline回去,

然後再看看這樣是不是比較好理解。

 

詳細的操作流程可以參考官網提供的範例

我覺得解說還蠻詳細的,

以下僅分享幾個筆者比較熟悉的重構手法:

 

Introduce

這個手法跟Inline剛好相反,

Inline是把變數回填到方法中,

Introduce則是把方法中值抽取出來。

Introduce可分為以下三種:

  • Introduce Parameter:將抽取成參數 (快捷鍵Ctrl+Alt+P)
  • Introduce Filed:將抽取成欄位(快捷鍵Ctrl+Alt+F)
  • Introduce Variable:將值抽取成變數(筆者目前還沒找到快捷鍵,若有大大知道再麻煩分享!)

 

通常我會在看不懂方法中「傳入值」想表達的意義時使用這個手法,

將其抽取出來,試著搭配命名來解釋「我想幹麻」。

 

Rename

快捷鍵Ctrl+R+R

重新命名看似最簡單,

其實是最難的事情。

一個好的命名應該明確的表達意圖

讓別人看你的程式碼就知道「你想幹嘛」,

而不需要任何註解和文件來補充說明。

但要一次命名就到位其實還是需要相當的經驗,

 

我個人會習慣在進行重構時,

試著以陌生人的角度來看,

只要我覺得不好理解,

或想到更好的命名,

就會把舊的命名給取代掉。

 

Extract Method

快捷鍵Ctrl+R+M

方法抽取是最常用的重構手法之一,

我通常會先把比較複雜的程式碼抽出來,

然後在針對那團程式碼逐一拆解,

歸納完主要的邏輯之後再看看是否要調整。

也可能把邏輯主幹歸納完之後再將不必要的變數Inline回去。
 

結語

就在完賽的前一週,

筆者剛好很巧的去上91哥的「重構與TDD實戰」,

也激起我想用「重構」這個主題做結尾的念頭。

 

講師在上課的過程中,

透過直接Live Coding的方式,

不斷的顛覆我對重構的定義,

你的重構不是我的重構

你以為重構只是Rename跟Extract Method嗎?

一段程式碼至少要被重構三次以上

距離上完課已經三天了,

這些聲音還在我耳邊圍繞。

 

重構會隨著每個人的功力而有不同的結果,

也許有些人會認為「後面有時間再來重構」,

但通常就不會有然後了。

重構應該要在當下的過程就進行,

否則後面再回來Code Review時,

還要花一次功去理解這段程式碼的含意。

 

ReSharper對於一個開發者的助益實在頗大,

但在費用上的確是一筆開銷。

最後我想引用91哥的一段話作為結語:

買來擺著不用,你就會覺得貴。

讓它(ReSharper)可以幫你賺錢,你就會覺得便宜。

參考

https://www.jetbrains.com/resharper/features/code_refactoring.html