简单干净的C#方法设计案例:SFCUI.AjaxLoadPage()之二
合并显而易见的代码
所谓显而易见的代码,就是看上去和别处相同的代码。
在这个例子中,就是View‘中初始页面显示的内容与未来刷新的内容重复;Controller中初始显示的运算和刷新的相同。
Controller好办,如此:
- print?private void PrepareAssignItemsData(int sprintID)
- {
- var sprint = ...
- var team = ...
- var overTimes = ...;
- var itemsTreeInSprint = ...
- ViewBag.AssignItemsViewModel = ...
- ViewBag.ViewModel = ...
- }
- public ActionResult AssignItems(int sprintID)
- {
- PrepareAssignItemsData(sprintID);
- return View("~/Areas/SFC/Views/Items/ItemTree.cshtml");
- }
- public ActionResult AjaxRefreshAssignItemsLeftPad(int sprintID)
- {
- PrepareAssignItemsData(sprintID);
- return View("~/Areas/Agile/Views/PlanningMeetings/AjaxRefreshAssignItemsLeftPad.cshtml");
- }
而cshtml页面中,只需要添加一个在页面初始化时自动刷新的函数,就能把<div id = "leftpad">变成空的,由刷新页面来填充。代码变成:
- [html] view plaincopyprint?<script type = "text/javascript">
- $(document).ready(function () {
- refreshLeft();
- });
- function refreshLeft() {
- $("#refreshLeft").click();
- };
- </script>
- <div style = "display: no1ne">
- @SFCUI.Link("refresh", "/Agile/PlanningMeetings/AjaxRefreshAssignItemsLeftPad?sprintID=" + assignItemsViewModel.Sprint.ID, ajaxUpdateTargetID: "leftpad", ajaxOnSuccess: "refreshAll", id: "refreshLeft")
- </div>
- <div id = "leftpad">
- </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叫什么,都无所谓。
所以,接口调用应该是:
- 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的源代码,里边多了一些参数,最后解释:
- print?public const string AJAX_LOAD_PAGE_CLASS = "ajaxloadpage";
- private static Random _rand = new Random();
- public static MvcHtmlString AjaxLoadPage(string pageLink, string refreshFunction = null, string ajaxOnSuccess = null, string style = null, int timeout = 0)
- {
- int id = _rand.Next();
- 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();
- TagBuilder script = new TagBuilder("script");
- script.MergeAttribute("type", "text/javascript");
- script.InnerHtml = "$(document).ready(function () { setTimeout(function () { $(\"#" + id.ToString() + "\").click(); }, " + timeout + "); });";
- if (refreshFunction != null)
- script.InnerHtml += "function " + refreshFunction + "() { $(\"#" + id.ToString() + "\").click(); };";
- html += script.ToString();
- TagBuilder pageContainer = new TagBuilder("div");
- pageContainer.MergeAttribute("id", id.ToString() + "Body");
- pageContainer.MergeAttribute("style", style);
- html += pageContainer.ToString();
- return new MvcHtmlString(html);
- }
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
简单干净的C#方法设计案例:SFCUI.AjaxLoadPage()之二相关推荐
- 简单干净的C#方法设计案例:SFCUI.AjaxLoadPage()之一
之一,之二 案例背景 本文将介绍如何通过一行代码实现下图中的功能. 红色框中是http://blog.csdn.net/cheny_com/article/details/7234619提到的Ajax ...
- 简单干净的C#方法设计案例:SFCUI.AjaxValue()之三
之一,之二,之三 之前讲到,方法声明为: @SFCUI.AjaxValue(story.ID, effortValue.ToString(), Effort.EffortPlannedValues, ...
- 简单干净的C#方法设计案例:SFCUI.AjaxValue()之二
之一,之二,之三 先重温一下原则: 最小信息原则:方法接口应只传递最必须的业务信息. 包括两个层面: 1. 技术信息不要暴露 2. 业务信息不能重复 业务信息分析 先别管技术问题,想一想做这个事情需要 ...
- 简单干净的C#方法设计案例:SFCUI.AjaxValue()之一
之一,之二,之三 有很多时候,需要我们在代码中执行很多重复的工作,这时候要涉及到如何使用可复用的方法来解决重复性问题. 比如一个项目管理系统,要求在不同的页面的不同位置都能调整任务的估算工作量,而且要 ...
- Ajax 解释Ajax的XML和JSON格式,操作Ajax的几种封装方法【案例+解释】(二)
文章目录 1.Ajax同步的使用 2.JS的单线程和事件队列 2.Ajax的数据格式 1.XML数据格式 2.JSON数据格式 3.如何处理XML数据格式案例 4.如何处理JSON数据格式案例 3.A ...
- 弹性方法计算内力例题_长江大学土木工程专业2008年钢筋混凝土楼盖课程设计案例...
长江大学土木工程专业2008年钢筋混凝土楼盖课程设计案例 钢筋混凝土楼盖课 程 设 计 指 导 书长江大学2008 年 8 月目 录第 1 节 设计任务书一.设计任务二.设计内容三.设计条件四.进度安 ...
- DDD系列 实战一 应用设计案例 (golang)
DDD系列 实战一 应用设计案例 (golang) 基于 ddd 的设计思想, 核心领域需要由纯内存对象+基础设施的抽象的接口组成 独立于外部框架: 比如 web 框架可以是 gin, 也可以是 be ...
- 易于使用的人工智能_需求分析:如何使用这种易于启动的方法+一个案例研究...
易于使用的人工智能 by Turgay Çelik 由TurgayÇelik 需求分析:如何使用这种易于启动的方法+一个案例研究 (Requirement Analysis: how to use t ...
- 怎么把线稿提取出来_PS教程:提取线稿如此简单?三种方法总有一种适合你
原标题:PS教程:提取线稿如此简单?三种方法总有一种适合你 部落窝教育,每天学习PS独家原创视频 在PS出现之前,如果我们喜欢某个动漫角色大概只能通过临摹的方式将人物线条勾勒出来,但PS的出现将提取线 ...
最新文章
- 公基考计算机知识吗,2021河北唐山事业单位公基备考知识:计算机常识
- 病毒入侵计算机的危害,那些年电脑中过的病毒,中病毒的危害又有哪些
- 小学教师计算机国培培训总结,小学教师国培个人研修总结
- BeagleBone Black快速入门教程第1章嵌入式Linux之于Maker们
- 设计模式——工厂模式
- xml教程之约束schema
- 【通知】深度学习之模型设计书籍勘误汇总!附赠资源和代码开源问题
- 封装Python列表实现多下标访问
- FFmpeg的H.264解码器源代码简单分析:宏块解码(Decode)部分-帧内宏块(Intra)
- Apache Jmeter压力测试
- 架构之美 | 按图索骥,就能做好架构图!
- WIndowsServer2012 DHCP服务器配置
- 【samba】Wodows同步Linux文件|搭建共享文件服务器——在windows上映射网络驱动器...
- java判断excel格式_Java 设置Excel数据验证
- continue语句可以用在switch语句和3种循环语句中_必须知道的C语言知识细节:break、continue语句区别...
- 浅谈OpenNI之我见
- taishan服务器修改为u盘启动,bios改成u盘启动
- CSS实现圆角,三角,五角星,五边形,爱心,12角星,8角星,圆,椭圆,圆圈,八卦等等
- JAVA开发与运维(JavaWeb测试环境搭建)
- 【信号与系统】5. 系统的特性、LTI系统分析