簡單幹淨的C#方法設計案例:SFCUI.AjaxLoadPage()之二

 

合併顯而易見的代碼

所謂顯而易見的代碼,就是看上去和別處相同的代碼。javascript

在這個例子中,就是View‘中初始頁面顯示的內容與將來刷新的內容重複;Controller中初始顯示的運算和刷新的相同。css

Controller好辦,如此:html

  
  
  
  
  1. print?private void PrepareAssignItemsData(int sprintID)   
  2. {   
  3.     var sprint = ...   
  4.     var team = ...   
  5.     var overTimes = ...;   
  6.     var itemsTreeInSprint = ...   
  7.     ViewBag.AssignItemsViewModel = ...   
  8.    
  9.     ViewBag.ViewModel = ...   
  10. }   
  11. public ActionResult AssignItems(int sprintID)   
  12. {   
  13.     PrepareAssignItemsData(sprintID);   
  14.     return View("~/Areas/SFC/Views/Items/ItemTree.cshtml");   
  15. }   
  16. public ActionResult AjaxRefreshAssignItemsLeftPad(int sprintID)   
  17. {   
  18.     PrepareAssignItemsData(sprintID);   
  19.     return View("~/Areas/Agile/Views/PlanningMeetings/AjaxRefreshAssignItemsLeftPad.cshtml");   
  20. }   

而cshtml頁面中,只須要添加一個在頁面初始化時自動刷新的函數,就能把<div id = "leftpad">變成空的,由刷新頁面來填充。代碼變成:java

  
  
  
  
  1. [html] view plaincopyprint?<script type = "text/javascript">   
  2.     $(document).ready(function () {   
  3.         refreshLeft();   
  4.     });   
  5.     function refreshLeft() {   
  6.         $("#refreshLeft").click();   
  7.     };   
  8. </script>   
  9. <div style = "display: no1ne">   
  10.     @SFCUI.Link("refresh", "/Agile/PlanningMeetings/AjaxRefreshAssignItemsLeftPad?sprintID=" + assignItemsViewModel.Sprint.ID, ajaxUpdateTargetID: "leftpad", ajaxOnSuccess: "refreshAll", id: "refreshLeft")   
  11. </div>   
  12.    
  13. <div id = "leftpad">   
  14. </div>   

$(document).ready是多出來的代碼,負責第一次初始化時填充<div id = "leftpad>的內容。程序員

封裝多餘的技術代碼

何爲多餘的技術代碼?以前在AjaxValue系列中曾經提到過接口封裝的兩個原則:ajax

最小信息原則:方法接口應只傳遞最必須的業務信息。編程

包括兩個層面:dom

1. 技術數據不要傳遞ide

2. 業務數據不能重複函數

 

如今,先從業務角度分析,這個函數接口設計中,到底哪些信息是必須的;讓咱們來用這一原則,把上面最後的一段cshtml代碼,變成一行代碼。

1. 若是要刷新,應該調用什麼函數(通常是一個JS函數,提供給左邊的紅框,紅框運行成功,就調用這個JS函數)

2. 用於刷新頁面的Ajax Url(上述JS函數被調用後,應該到哪一個Url獲取刷新內容)

3. 刷新成功後,要繼續執行什麼操做(另一個JS函數,好比刷新的內容「錯過了document.Ready」要從新套用一下——嘗試了live功能,不知道爲何無效;或者要串聯刷新多個區域,本例中沒有)

沒了(後面還會看到一些,不過是爲了別的功能)。有幾個東西是多餘的:

1. $(document).ready.....,由於每一個AjaxPageLoad都執行,因此是固定的,不用做爲參數傳入接口。

2. function refreshLeft() ...{ $"#refreshLeft")....,由於這個按鈕是多餘的,並不須要人手去點擊,只是函數實現中的一個步驟而已。換言之這個按鈕叫什麼都無所謂,只要ready / function / SFCUI.Link(id: ...)中出現的值相同就行,無需人工指定。

3. <div id = "leftpad"></div>也是多餘的!由於既然在這裏寫下那一行代碼,就在代碼處LoadPage,至於Load到什麼東西里邊,ID叫什麼,都無所謂。

因此,接口調用應該是:

  
  
  
  
  1. print?@SFCUI.AjaxLoadPage("/Agile/PlanningMeetings/AjaxRefreshAssignItemsLeftPad?sprintID=" + assignItemsViewModel.Sprint.ID, refreshFunction: "refreshLeft", ajaxOnSuccess: "refreshAll")   

這句話是一個helper,將負責生產前面代碼中提到的<script>及其中的兩個函數ready和refreshLeft、中間的<div>@SFCUI.Link中的Ajax調用</div>、最後的<div id = "leftpad">

下面是Helper的源代碼,裏邊多了一些參數,最後解釋:

  
  
  
  
  1. print?public const string AJAX_LOAD_PAGE_CLASS = "ajaxloadpage";   
  2. private static Random _rand = new Random();   
  3. public static MvcHtmlString AjaxLoadPage(string pageLink, string refreshFunction = null, string ajaxOnSuccess = null, string style = null, int timeout = 0)   
  4. {   
  5.     int id = _rand.Next();   
  6.     string html = SFCUI.Link("refresh", "/SFC/Ajax/AjaxLoadPage?pageLink=" + HttpUtility.UrlEncode(pageLink), ajaxUpdateTargetID: id.ToString() + "Body", ajaxOnSuccess: ajaxOnSuccess, cssClass: "hide " + AJAX_LOAD_PAGE_CLASS + " ", id: id.ToString()).ToString();   
  7.     TagBuilder script = new TagBuilder("script");   
  8.     script.MergeAttribute("type", "text/javascript");   
  9.     script.InnerHtml = "$(document).ready(function () { setTimeout(function () {  $(\"#" + id.ToString() + "\").click(); }, " + timeout + "); });";   
  10.     if (refreshFunction != null)   
  11.         script.InnerHtml += "function " + refreshFunction + "() { $(\"#" + id.ToString() + "\").click(); };";   
  12.     html += script.ToString();   
  13.     TagBuilder pageContainer = new TagBuilder("div");   
  14.     pageContainer.MergeAttribute("id", id.ToString() + "Body");   
  15.     pageContainer.MergeAttribute("style", style);   
  16.     html += pageContainer.ToString();   
  17.     return new MvcHtmlString(html);   
  18. }   

1. 先看忽然跳出來的int id = _rand.next()

以前提到,咱們有不少「隨便」的變量,好比那個"refreshLeft",叫什麼都行,外界不關心。可是若是設爲常數,則若是在同一個頁面上放兩個LoadPage的時候,會打架,因此用個Random生成ID。爲何用static 的呢?由於Random的生成機制,是利用調用時的系統時間做爲「種子」,從種子產生下一個隨機數。若是調用時間間隔爲「0」,就會產生出相同的種子進而相同的隨機數。static就沒有這個問題了,你們用一個,順着排。

總之,隨機的id代替了"refreshLeft"這個原本須要傳進來的「變量」。

2. TagBuilder script則直接在代碼上方產生一段script,免去了編寫script的工做。

爲何要判斷refreshFunction爲NULL的狀況呢?由於有一種場景不會重複刷新。

在個人項目中,菜單極其複雜,產生菜單的時間,甚至比頁面自己都長。但咱們也不像犧牲強大的菜單換取性能,怎麼辦呢?子菜單延時產生!

因爲人們在頁面出現後的1~5秒內都不會操做菜單(要離開這個頁面時纔會操做),因此咱們設置全部繁重的子菜單都使用AjaxLoadPage加載,並且注意最後一個參數timeout,它們多數在1000毫秒後才加載,那時候頁面早就爽快地展現在用戶面前了。

這種場景,不會有人刷新菜單了,因此就不用RefreshFunction這個參數。

3. TagBuilder pageContainer 代替了原來最後的<div id = "leftpad">,它的id也是隨機生成的,可是因爲一、二、3中保持了id的互相照應,雖然外界看不到,可是內部卻順暢運行。

4. 最後多了個style,是咱們加載菜單時的須要,這裏就很少說了。

尾聲

合併顯而易見的代碼是初級程序員的基本功,也是產品代碼的基本要求;封裝多餘的技術代碼難度較大,可是隻要用心,上述提到的原則也沒有作不到的。

爲何要費勁封裝呢?散裝的代碼對程序員要求低,只要好好測試,功能相同,也不會出現缺陷,不也同樣嗎?

在16年的IT從業中我發現,全部最後開發面臨崩潰的軟件,不多有受到單個技術難題或缺陷困擾的,多數都百病纏身;而百病纏身的主要問題,是可維護性差;而可維護性差的主要緣由,是代碼臃腫重複,尤爲是似重複而不重複。

封裝後,則:

1. 老是使用同一段代碼,易於維護。

2. 老是重複使用,代碼的質量有保障。

3. 一個地方發現缺陷,修改代碼後能夠同時避免多個地方的缺陷。

4. 在無需深刻到技術層面的時候,能夠方便地在業務層面閱讀代碼。

5. 把時間花費在深究代碼的封裝上,比重複碼字要有趣得多。

……

在以前的IT職業生涯的「危險職業」系列中有不少人提到:「我一直在作重複勞動,沒有積累,是否是很危險?怎麼辦?」其實萬事萬物看似重複,其實不重複。本人從事Web編程只有一年多(其中只有6個月的編程量超過總工做量的50%),Jquery上上個月剛大體弄明白,可是我相信不少Web編程好久的程序員都不會封裝這些代碼的,而是「重複地」拷貝粘貼代碼。

因此實際上,沒有重複的工做,只有重複的工做心態。

以後我會寫一篇關於「重複勞動中如何提升」的IT職業生涯系列文章。 

相關文章
相關標籤/搜索