简单干净的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

相关文章
|
4月前
|
开发框架 .NET 程序员
C# 去掉字符串最后一个字符的 4 种方法
在实际业务中,我们经常会遇到在循环中拼接字符串的场景,循环结束之后拼接得到的字符串的最后一个字符往往需要去掉,看看 C# 提供了哪4种方法可以高效去掉字符串的最后一个字符
409 0
|
3月前
|
编译器 C#
C#多态概述:通过继承实现的不同对象调用相同的方法,表现出不同的行为
C#多态概述:通过继承实现的不同对象调用相同的方法,表现出不同的行为
137 65
|
2月前
|
JSON 程序员 C#
使用 C# 比较两个对象是否相等的7个方法总结
比较对象是编程中的一项基本技能,在实际业务中经常碰到,比如在ERP系统中,企业的信息非常重要,每一次更新,都需要比较记录更新前后企业的信息,直接比较通常只能告诉我们它们是否指向同一个内存地址,那我们应该怎么办呢?分享 7 个方法给你!
|
2月前
|
C# UED SEO
C# 异步方法async / await任务超时处理
通过使用 `Task.WhenAny`和 `Task.Delay`方法,您可以在C#中有效地实现异步任务的超时处理机制。这种方法允许您在指定时间内等待任务完成,并在任务超时时采取适当的措施,如抛出异常或执行备用操作。希望本文提供的详细解释和代码示例能帮助您在实际项目中更好地处理异步任务超时问题,提升应用程序的可靠性和用户体验。
90 3
|
3月前
|
存储 C#
【C#】大批量判断文件是否存在的两种方法效率对比
【C#】大批量判断文件是否存在的两种方法效率对比
59 1
|
3月前
|
C#
C#的方法的参数传递
C#的方法的参数传递
36 0
|
3月前
|
数据可视化 程序员 C#
C#中windows应用窗体程序的输入输出方法实例
C#中windows应用窗体程序的输入输出方法实例
63 0
|
2月前
|
C# 开发者
C# 一分钟浅谈:Code Contracts 与契约编程
【10月更文挑战第26天】本文介绍了 C# 中的 Code Contracts,这是一个强大的工具,用于通过契约编程增强代码的健壮性和可维护性。文章从基本概念入手,详细讲解了前置条件、后置条件和对象不变量的使用方法,并通过具体代码示例进行了说明。同时,文章还探讨了常见的问题和易错点,如忘记启用静态检查、过度依赖契约和性能影响,并提供了相应的解决建议。希望读者能通过本文更好地理解和应用 Code Contracts。
44 3
|
21天前
|
存储 安全 编译器
学懂C#编程:属性(Property)的概念定义及使用详解
通过深入理解和使用C#的属性,可以编写更清晰、简洁和高效的代码,为开发高质量的应用程序奠定基础。
77 12
|
2月前
|
设计模式 C# 图形学
Unity 游戏引擎 C# 编程:一分钟浅谈
本文介绍了在 Unity 游戏开发中使用 C# 的基础知识和常见问题。从 `MonoBehavior` 类的基础用法,到变量和属性的管理,再到空引用异常、资源管理和性能优化等常见问题的解决方法。文章还探讨了单例模式、事件系统和数据持久化等高级话题,旨在帮助开发者避免常见错误,提升游戏开发效率。
78 4