简单干净的C#方法设计案例:SFCUI.AjaxLoadPage()之二-阿里云开发者社区

开发者社区> 开发与运维> 正文

简单干净的C#方法设计案例:SFCUI.AjaxLoadPage()之二

简介:

 

合并显而易见的代码

所谓显而易见的代码,就是看上去和别处相同的代码。

在这个例子中,就是View‘中初始页面显示的内容与未来刷新的内容重复;Controller中初始显示的运算和刷新的相同。

Controller好办,如此:


  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">变成空的,由刷新页面来填充。代码变成:


  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系列中曾经提到过接口封装的两个原则:

最小信息原则:方法接口应只传递最必须的业务信息。

包括两个层面:

1. 技术数据不要传递

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也是随机生成的,但是由于1、2、3中保持了id的互相照应,虽然外界看不到,但是内部却顺畅运行。

4. 最后多了个style,是我们加载菜单时的需要,这里就不多说了。

尾声

合并显而易见的代码是初级程序员的基本功,也是产品代码的基本要求;封装多余的技术代码难度较大,但是只要用心,上述提到的原则也没有做不到的。

为什么要费劲封装呢?散装的代码对程序员要求低,只要好好测试,功能相同,也不会出现缺陷,不也一样吗?

在16年的IT从业中我发现,所有最后开发面临崩溃的软件,很少有受到单个技术难题或缺陷困扰的,多数都百病缠身;而百病缠身的主要问题,是可维护性差;而可维护性差的主要原因,是代码臃肿重复,尤其是似重复而不重复。

封装后,则:

1. 总是使用同一段代码,易于维护。

2. 总是重复使用,代码的质量有保障。

3. 一个地方发现缺陷,修改代码后可以同时避免多个地方的缺陷。

4. 在无需深入到技术层面的时候,可以方便地在业务层面阅读代码。

5. 把时间花费在深究代码的封装上,比重复码字要有趣得多。

……

在之前的IT职业生涯的“危险职业”系列中有很多人提到:“我一直在做重复劳动,没有积累,是不是很危险?怎么办?”其实万事万物看似重复,其实不重复。本人从事Web编程只有一年多(其中只有6个月的编程量超过总工作量的50%),Jquery上上个月刚大致弄明白,但是我相信很多Web编程很久的程序员都不会封装这些代码的,而是“重复地”拷贝粘贴代码。

所以实际上,没有重复的工作,只有重复的工作心态。

之后我会写一篇关于“重复劳动中如何提高”的IT职业生涯系列文章。 



本文转自火星人陈勇 51CTO博客,原文链接:http://blog.51cto.com/cheny/1101508

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

分享:
开发与运维
使用钉钉扫一扫加入圈子
+ 订阅

集结各类场景实战经验,助你开发运维畅行无忧

其他文章