作者:陳勇
出處:blog.csdn.net/cheny_com
?
這是編碼簡單性系列中的其中一篇,之前幾篇包括代碼篇和語義篇。
因為要積累案例,會隨時更新。
?
之前提到:編碼簡單性的“心法”就是:只要屏幕上有任何兩部分代碼看上去相似,則一定有合并辦法。
但在代碼層面,無論如何簡化,都常常剩下一堆相似代碼,這時候就需要編寫函數了。
這里指的函數,包括HtmlHelper這類用C#解決Html的函數。
?
應隨時關注代碼中的“不簡潔”現象,一旦放任其發生,軟件將很難維護。
案例1 從Html代碼抽取MVC HtmlHelper
為了在屏幕上顯示一些大按鈕(下方顯示其文字),編寫了這些代碼。為了對齊/消除邊框/Hover效果等,動用了幾個class來設置格式。按鈕很多,所以需要相當數量的拷貝粘貼,其中文字-alt-title還是重復的,每次都要改動三個地方……
這些,都是代碼正在變復雜的壞味道。
?
以其中兩次重復為例,說明修改方法:
??????????? <td class = "noborder nopaddingv">
??????????????? <div class = "aligncenter">
??????????????????? <a href="/Home/Index">
??????????????????????? <img src="../../Resouces/Images/Agile/Menu/Campass48.png" alt = "站點地圖" title = "站點地圖" class = "imagelink"/></a><br />
??????????????????????? 站點地圖
??????????????? </div>
??????????? </td>
??????????? <td class = "noborder nopaddingv">
??????????????? <div class = "aligncenter">
??????????????????? <a href="/Home/Index" target = "_blank">
??????????????????????? <img src="../../Resouces/Images/Agile/Help/Help48.png" alt = "幫助" title = "幫助" class = "imagelink"/></a><br />
??????????????????????? 幫助
??????????????? </div>
??????????? </td>
手段就是做HtmlHelper(以前沒做過復雜的,所以推到現在才做),結果是:
?
??????? @Html.MenuImageButtonV("站點地圖", false, "/Home/Index", "../../Resouces/Images/Agile/Menu/Campass48.png")
??????? @Html.MenuImageButtonV("幫助", true, "/Home/Index", "../../Resouces/Images/Agile/Help/Help48.png")
Helper的內容就不展示了,網上一查都會。唯一說明的就是class變成了style,因為在函數內部引用class是危險的,不能保證被復用后class也在。
突然想起來當年曾經寫過一句話很好地概括了這種技法:當兩段代碼相同時,可以變成一個函數和兩次調用,相同的部分就是函數,不同的部分就是調用參數。
可以看到本例中,只有文字(title/alt)、是否新窗口打開、鏈接、圖片不同,所以就是一個有四個參數的函數。
?
案例2 2011-05-04 函數接口的簡潔性?
不是編寫了函數就萬事大吉了,不好的函數接口依然不簡潔。下面是一個剛改好的例子:
@Html.ImageLink("燃燒圖", true, "../../Resouces/Images/Agile/CurrentSprint/BurndownData16.png", "icons", false, "/Agile/CurrentSprint/BurndownData", null, null)
@Html.ImageLink("迭代計劃", true, "../../Resouces/Images/Agile/Sprints/Index16.png", "icons", false, "/Agile/Sprints/Index", null, null)
...
像這樣的函數重復上幾回也很讓人頭痛的,關鍵臭長的函數還看不到尾部,很容易出錯或影響閱讀。
這時候的技法是:如果函數在重復調用時有些參數變化,有些參數基本不變("icons", true, false, null....),后者應該變成一些可選參數并設置缺省值。
修改后的結果(另外一段代碼):
@Html.ImageLink("迭代計劃", "/Agile/Sprints/Index", true) //這個true有些地方不太一樣所以留下了。
@Html.ImageLink("迭代故事", "/Agile/Sprints/SprintStories", true)
...
唯一的不太好理解的是圖片的鏈接哪去了。我在后臺目錄做了一套命名規則,若imgPath為缺省值null,則會根據命名規則生成默認圖片名稱,保證圖片可以用Area/Controller/Actoin定位。這樣也簡化了實現和維護,不用去在腦海里便維護一套圖片與鏈接的對應關系,也是編碼簡單性的一種體現。
案例3 2011-05-04 MS Chart Winform版本與MVC3 Razor Helper的比較
在Winform版本中,一個Chart除了要在aspx中放置控件外,還要這樣設置參數:
Chart1.Series["Series1"].ChartType = (SeriesChartType) Enum.Parse( typeof(SeriesChartType);
Chart1.Series["Series2"].ChartType = (SeriesChartType) Enum.Parse( typeof(SeriesChartType); ChartTypeList.SelectedItem.Text, true );
Chart1.Series["Series1"].IsValueShownAsLabel = true;
Chart1.Series["Series2"].IsValueShownAsLabel = true;
Chart1.Series["Series1"]["LabelStyle"] = PointLabelsList.SelectedItem.Text;
Chart1.Series["Series2"]["LabelStyle"] = PointLabelsList.SelectedItem.Text;
...
這是非常難讀的代碼,至少相當的不直觀,MVC3的程序員顯然要厲害得多(應該是MS意識最好的程序員了,要不做好不MVC),他們的代碼長得這個樣子:
new Chart(400, 200, ChartTheme.Blue)
.AddTitle(title)
.DataBindTable(data, "X")
.Write("png");
看來是由于工作量問題,beta版本實際上只實現了很有限的幾個函數,但實現得卻很漂亮。按照這種風格寫上面那個Chart1,應該是(僅展示風格):
new Chart()
??? .AddSeries( new Series("Series1",
??????? ChartType: (SeriesChartType) Enum.Parse( typeof(SeriesChartType)),
??????? IsValueShownAsLabel: true)
??????? .AddLable( new Label(
??????????? LableStyle:PointLabelsList.SelectedItem.Text)
??? ....
這種風格很類似JQuery中的風格,是一種“寫得少,做得多”的風格,其心法就是:代碼里邊沒有任何兩個地方看上去相似。
案例4 2011-05-05 函數接口不簡潔的深層原因
01年左右曾經有一家國內排名前3的家電廠商做了一款機頂盒在我們公司聯合測試。此機頂盒穩定性很差,其程序員長駐現場有問題就改,而問題也從不停息,比同期測試的其他幾家機頂盒差很多。一年后有一個偶然的機會被請去評價一段機頂盒代碼是否可用,從函數名稱等蛛絲馬跡判斷就是這個廠家的代碼,答案是:不可用。
那些代碼外觀的第一感覺是像一篇小說,每行都很長,能自成段落的都有,有點像案例3中開始不好的代碼且更甚之,最后對代碼進行了局部改造,量化評價的結果是實際所需代碼大概是已有代碼的1/6。
怎么才能變成案例3里邊后面那種長得像詩詞而不是小說的代碼呢?
當時我們分析那段機頂盒代碼,問題出在類的封裝上,總結一下包括:
1. 類的內聚性不好,多數函數屬于哪個類都不好,因為哪個類的變量都不足以支撐計算,只得在單獨聲明一個函數(當時是用C++,C#不允許了),傳入大量類。
2. 類的封裝不好,多數類都使用public的內部數據,很容易出現Chart1.Series["Series2"]["LabelStyle"] = ...這種從chart挖到Series,從Series挖到LableStyle才能干活的情況,所以函數調用臭長。
再深入挖掘一下形成這種結果的原因,在于多數程序員(本人也一樣)都喜歡先從類的變量而非類的函數開始設計,最終導致類更適合存放數據而不適合做事。一種解決辦法就是所謂調用驅動開發,就是先把要調用的代碼寫出來,打樁,再實現。
?
點擊下載免費的敏捷開發教材:《火星人敏捷開發手冊》
?