在學習程式設計的路上,相信大家這幾個詞一定不少聽,什麼 面相物件、封裝繼承多型、內功心法21種設計模式 等等 。但是卻很少用到,或者說用到的都是被動使用。大牛們在寫程式碼前早就構思好了,介面,基類等等。自己寫程式碼的時候,很少有把物件導向想的面很全,很容易在遇上不夠優秀的程式碼,這時候就需要重構了。
但是我們卻很少去重構,可能原因有很多,比如很重要的一點:不想改出Bug;不想增加工作量(我是要5點半下班的男人,女朋友還在等我做飯);時間很緊,先實現功能先;程式碼是82年的不敢動!!!
其實重構可以寫出更健壯的程式碼、減少後面的工作量、讓開發者更好閱讀。看了很多重構的文章,發現很多是一些基本的,命名規範或者拆函式什麼的。這篇文章寫下我重構的一些思路和重構之前程式碼對比。好了,廢話不多說,上專案。威武、威武、威武........
簡單介紹一下專案,專案就是一個客戶端小工具用來稽核編寫遞交的說明是否規範。這裡說下,物件導向是一種思想,和語言無關。只要是面嚮物件語言,無論你是C#、Java、TypeScript、Python,都是可以用這種思想重構的。
上圖就是小工具了一個部分截圖,有若干個欄,每個欄都是填寫一些對應的修改內容,在稽核校驗時,會檢查寫的內容是否符合標準。
前輩已經完成一些欄的校驗,我的任務是完成剩下欄:寫正規表示式,然後在不標準的時候提示就好了,是不是覺得根本沒有必要重構,依葫蘆畫瓢就好了在我拿到程式碼的時候,是這樣的程式碼:
// 下面變數是和介面繫結的變數,RaisePropertyChanged作用在變數改變的時候通知前端重新渲染
// 不熟悉C#程式碼的,只用知道這些變數就是和前臺繫結的就是了
private string _editDescription;
/// <summary>
/// 修改說明內容
/// </summary>
public string EditDescription
{
get { return _editDescription; }
set
{
_editDescription = value;
RaisePropertyChanged(() => EditDescription);
}
}
private string _editDescriptionRules;
/// <summary>
/// 修改說明校驗規則
/// </summary>
public string EditDescriptionRules
{
get { return _editDescriptionRules; }
set
{
_editDescriptionRules = value;
RaisePropertyChanged(() => EditDescriptionRules);
}
}
private bool _editDescriptionHasValidationError;
/// <summary>
/// 修改說明校驗標誌
/// </summary>
public bool EditDescriptionHasValidationError
{
get { return _editDescriptionHasValidationError; }
set
{
_editDescriptionHasValidationError = value;
RaisePropertyChanged(() => EditDescriptionHasValidationError);
}
}
private string _integratedNote;
/// <summary>
/// 整合注意內容
/// </summary>
public string IntegratedNote
{
get { return _integratedNote; }
set
{
_integratedNote = value;
RaisePropertyChanged(() => IntegratedNote);
}
}
private string _integratedNoteRules;
/// <summary>
/// 整合注意規則
/// </summary>
public string IntegratedNoteRules
{
get { return _integratedNoteRules; }
set
{
_integratedNoteRules = value;
RaisePropertyChanged(() => IntegratedNoteRules);
}
}
private bool _integratedNoteHasValidationError;
/// <summary>
/// 整合注意校驗標誌
/// </summary>
public bool IntegratedNoteHasValidationError
{
get { return _integratedNoteHasValidationError; }
set
{
_integratedNoteHasValidationError = value;
RaisePropertyChanged(() => IntegratedNoteHasValidationError);
}
}
// 這裡隨便舉了兩欄的變數,後面還有若干欄。
複製程式碼
依葫蘆畫瓢以後呢,我發現原來是這樣的。每一欄用了單獨三個變數直接去繫結:編寫的內容、是否標準的標誌、不標準提示語。我是一個懶人啊,在我畫了兩個瓢以後,就很煩(一直在那複製變數,在那改來改去),這些個變數都是差不多一個意思。為啥讓我重複在複製,修改呢?
明顯每欄有相同項啊,對不對,一個是內容,一個狀態,一個是錯誤提示語啊,咆哮!!!
這時候,我想起了書本上的一句話: "早重構,常重構"
我已經安奈不住我那顆懶惰的心裡,因為下面還有“狠多狠多”欄,每一行有三個類似的變數,我這依葫蘆畫瓢,這個星期加班,就在複製貼上去了。我是要上進,要每天進步的人,不能這樣!
我為啥不能將這相同的共性抽象出來呢?是不咯,這個時候,我想起了《葵花寶典》的第一頁的第一句:“萬物皆物件”,於是本能的告訴我,每一欄看做一個物件,只是每欄的值不一樣。然後我就寫了一個”雞肋“(基類),程式碼如下:
/// <summary>
/// 欄 基類
/// </summary>
public class Base: ObservableObject
{
private string _content;
/// <summary>
/// 內容
/// </summary>
public virtual string Content
{
get { return _content; }
set
{
_content = value;
RaisePropertyChanged(() => Content);
}
}
private string _errorTip;
/// <summary>
/// 錯誤提示
/// </summary>
public virtual string ErrorTip
{
get { return _errorTip; }
set
{
_errorTip = value;
RaisePropertyChanged(() => ErrorTip);
}
}
private bool _isError;
/// <summary>
/// 是否錯誤
/// </summary>
public virtual bool IsError
{
get { return _isError; }
set
{
_isError = value;
RaisePropertyChanged(() => ErrorTip);
}
}
}
複製程式碼
virtual是為了讓子類能夠重寫get和set(如果有需求的話,為後面擴充套件做準備),然後欄位從3個變到了1個了。
/// <summary>
/// 修改說明欄
/// </summary>
public class EditDescription : Base { }
private EditDescription _editDescriptions;
/// <summary>
/// 修改說欄繫結變數
/// </summary>
public EditDescription EditDescriptions
{
get { return _editDescriptions; }
set
{
_editDescriptions = value;
RaisePropertyChanged(() => EditDescriptions);
}
}
// 其他的一樣,我就不多寫了
複製程式碼
那,我們來算一下賬,原先的變數,每一欄有3個變數,一個變數有6行程式碼的話,假如我這個有100欄,就是:
重構前: 100(欄)x3x6 = 1800 行程式碼 (阿西吧!!!)。
重構後: 100(欄)x1x6 = 600 行程式碼 。
小學算數: 1800 - 600 = 1200 (1200行,你說乾點啥不好啊)
那秀兒們算下,你花這麼多時間,在那複製貼上,不敢去動前輩們的程式碼,還是勇敢一點呢?
然後是不是感覺到一個繼承就簡單了很多呢,這只是一個物件導向的簡單運用,就讓我們少寫了這麼多程式碼。
前輩和我說,寫程式碼就像練武功,就像你會了“九陽神功”或者"吸星大法",學其他武功看一眼就會。也就是說,當你理解了物件導向以後呢,你自然而然的就會寫出很精簡的程式碼了。阿西吧,又扯遠了。
變數好了,抬頭一看函式,我的臉便有點抽搐,啊!!這函式有毒!函式是這樣的:
// 此處函式用來設定每一欄報錯時邊框變紅
private void SetValidateFlag()
{
// 繫結的實體類判斷狀態
if (tsEntity.EditDescriptionHasValidationError)
{
// 控制元件邊框改顏色
EditDescriptionHsExpander.BorderThickness = new Thickness(1);
EditDescriptionHsExpander.BorderBrush = _readBrush;
}
if (tsEntity.TestSuggestionHasValidationError)
{
TestSuggestionHsExpander.BorderThickness = new Thickness(1);
TestSuggestionHsExpander.BorderBrush = _readBrush;
}
if (tsEntity.IntegratedNoteHasValidationError)
{
IntegratedNoteHsExpander.BorderThickness = new Thickness(1);
IntegratedNoteHsExpander.BorderBrush = _readBrush;
}
// 此處省略一萬個if,每個欄都有一個if
}
複製程式碼
然後大家懂的嘛,在我改了兩欄以後,我又耐不住性子了。再一看,感覺似曾相識燕歸來的感覺啊!有沒有,每個if中都有三個類似的東西。我那個心啊,又忍不住悸動了起來,像是等初戀的感覺,想她來,來了又不知道要幹哈。然後我發現其實if中判斷的就是每欄的狀態,括號裡面是對控制元件欄的設定。然後想嘛,能不能搞個類似迴圈的東西,只要寫一個for就好了呢。
思考以後,是這樣的,我把控制元件也變成了欄的一個屬性了,這樣if判斷裡面就都是判斷一個類了。程式碼就變成了這樣:
public class Base: ObservableObject
{
/// <summary>
/// 模組控制元件(新增)
/// </summary>
public object Control { get; set; }
//其他的和上面的Base一樣
}
複製程式碼
然後 SetValidateFlag() 函式就變成了這樣:
/// <summary>
/// 設定校驗介面效果
/// </summary>
private void SetValidateFlag()
{
// listBase 所有欄實體集合
foreach (var item in listBase)
{
if (item.IsError)
{
var control = item.Control as HsExpander;
if (control == null)
continue;
// 將控制元件的邊框變成紅色
control.BorderThickness = new Thickness(1);
control.BorderBrush = _readBrush;
}
}
}
複製程式碼
好了嘞,好好的一個if的葫蘆瓢給我們給整沒了。這時候我們來算算我們這個重構,省了多少事。老樣子:
原先的一個if算5行程式碼,我這個有100欄,就是:
重構前: 100(欄)x 5 = 500 行程式碼 (全是if啊)。
重構後: 我數了一下,沒有錯的話應該是:14行程式碼 。
重構一下以後,變數得到了減少且對外統一。感覺一顆心得到了小小滿足,感覺靈魂得到了昇華,不知道是自己太容易滿足,還是程式碼世界裡給我的成就感。感覺“乾坤大挪移”瞬間上了兩層。
這就是我在寫程式碼的時候一個小小的重構思路,希望能夠幫助到大家一點。然後這個是我的Github,如果有幫助到大家的專案,可以給我點個star, 小生在這邊謝謝啦!!!