最近在 code review 的過程中,總是常常會看到一些令人驚喜的事情,其中一件事情和程式的寫法有關.有時候是邏輯可以在簡化,有時候是 coding style 上可以在簡化以便閱讀.首先來看 coding style 可以簡化的部份.在寫程式的過程中,有時會因為業務邏輯面的複雜而造成會寫出多個 nested if statements 的情況,如下面的例子
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
string GetVersionName(string revision, string version, string pass) | |
{ | |
string versionName = string.Empty; | |
if (!string.IsNullOrEmpty(revision.Trim()) && revision.Trim().ToLower() != "n/a") | |
{ | |
versionName = version + "," + revision + "," + pass; | |
} | |
else | |
{ | |
if (!string.IsNullOrEmpty(pass.Trim()) && pass.Trim().ToLower() != "n/a") | |
{ | |
versionName = version + "," + revision + "," + pass; | |
} | |
else | |
{ | |
versionName = version; | |
} | |
} | |
return versionName; | |
} |
上面的程式碼其實沒錯,也算方便閱讀.也許是我個人的因素,遇到 nested if 的時候,我會儘量地朝向少點縮排的方式前進. 除此之外,第 7 行和第 13 行是回傳一樣的答案.在程式寫法上可以將他們結合在一起,因為那是一個 "業務邏輯",一般來說,我們希望同一件事情只要發生在一個地方.因此,可以將上述的程式碼改成如下:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
string GetVersionName(string revision, string version, string pass) | |
{ | |
if (!string.IsNullOrWhiteSpace(revision) && !string.Equals(revision, "n/a", StringComparison.OrdinalIgnoreCase) | |
|| (!string.IsNullOrWhiteSpace(pass) && !string.Equals(pass, "n/a", StringComparison.OrdinalIgnoreCase)) | |
{ | |
return version + "," + revision + "," + pass; | |
} | |
return version; | |
} |
一個 if 配合上多個檢查條件,我自己覺得這樣閱讀起來比上述的語法好些.
再來看另外一個例子.
原本的程式碼如下:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
object GetColumnValue(SqlDataReader reader, PropertyInfo prop) | |
{ | |
object target = null; | |
if (reader.ColumnExists(prop.Name)) | |
{ | |
if (reader[prop.Name] == DBNull.Value) | |
{ | |
if (prop.PropertyType == typeof(string)) | |
{ | |
target = string.Empty; | |
} | |
else | |
{ | |
if (prop.PropertyType.IsValueType) | |
{ | |
target = Activator.CreateInstance(prop.PropertyType) | |
} | |
else | |
{ | |
target = null; | |
} | |
} | |
} | |
else | |
{ | |
target = reader[prop.Name]; | |
} | |
} | |
return target; | |
} |
同樣的,這程式也沒錯.不過,閱讀起來有點 "階梯".因為當我們閱讀時,大腦會產生那種 "階梯",如果 if true part/false part 的內容物很多程式碼,我們還得仔細看縮排才知道下一行要跑到那繼續看.
其實,只要動一點腦筋想一下就可以把它寫的更簡單,如下
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
object GetColumnValue(SqlDataReader reader, PropertyInfo prop) | |
{ | |
if (!reader.ColumnExists(prop.Name)) | |
{ | |
return null; | |
} | |
if (reader[prop.Name] != DBNull.Value) | |
{ | |
return reader[prop.Name]; | |
} | |
if (prop.PropertyType == typeof(string)) | |
{ | |
return string.Empty; | |
} | |
if (prop.PropertyType.IsValueType) | |
{ | |
return Activator.CreateInstance(prop.PropertyType); | |
} | |
return null; | |
} |
這樣的寫法是一樣的邏輯,而且是不是覺得更方便閱讀呢 ?
在寫程式的過程式,會常用到 if..else.. 是很正常的事情,然後這也代表你的業務邏輯將分成兩塊,而這兩塊的內容是獨立而不會互相干擾的,所以這兩塊的內容基本上不會重覆.若有重覆,那就是一般所謂的 duplicated code bad smell.另外,利用減少 "縮排" (indentation) 的技巧也能讓程式碼達到更方便閱讀的地步.
0 意見:
張貼留言