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

简介:

 之前讲到,方法声明为:


 
 
  1. print?@SFCUI.AjaxValue(story.ID, effortValue.ToString(), Effort.EffortPlannedValues, "/SFC/Efforts/AjaxSetEffortPlanned?itemID=" + story.ID + "&value={0}", ajaxOnSuccess: "refreshLeftPad");   

调用的例子:


 
 
  1. csharp] view plaincopyprint?@SFCUI.AjaxValue(story.ID, effortValue.ToString(), Effort.EffortPlannedValues, "/SFC/Efforts/AjaxSetEffortPlanned?itemID={0}&value={1}", ajaxOnSuccess: "refreshLeftPad");   

感觉上toggle、弹出菜单、UpdateTargetID什么的都没有啊,怎么能实现?

这就是设计的一种重要原则:把所有技术细节都隐藏在实现的内部。

函数内部的设计

下面的代码仅作示例,与我自己用的不太一样,简化了一下,只为说明问题。


 
 
  1. print?        public static MvcHtmlString AjaxValue(int id, string value, string[] values, string urlFormat, string ajaxOnSuccess = null)   
  2.         {   
  3.             string ajaxUpdateTargetID = "ValueOf" + id;   
  4.             string innerHtml = null;   
  5.    
  6.             //Build the div to show the final value.    
  7.             TagBuilder theValue = new TagBuilder("div"); //这个是放置原始数值的DIV,日后更新的也是它,ID在前面生成好了。    
  8.             theValue.InnerHtml = value;   
  9.             ...   
  10.             //Build the ajaxLink to popup the menu of values to be selected.    
  11.             TagBuilder ajaxLink = new TagBuilder("div"); //这个是在Value后面放一个可以点的小图片,点一下就会弹出菜单    
  12.             ...            ajaxLink.AddCssClass("clickshow"); //这个是我自己定义的Class,说明点击它,会弹出一样东西,就是是$("#clickshowBodyOfAjaxLinkOf" + $(this).attr(id)).show()。    
  13.             innerHtml += ajaxLink.ToString();   
  14.             //Build the div to show the popup menu; and a "clickshowbody" container to hide the menu when mouse left.    
  15.             TagBuilder content = new TagBuilder("div"); //这个是平时隐藏,点击后会弹出的那个菜单。    
  16.             ... //这里隐藏的比较多,因为我调用了很多我自己的函数比如ImageLink()等,写出来也看不到细节。    
  17.             ... //大致就是:在菜单里边foreach(var v in  values),用id 和每个v,string.Format(urlFormat, id, v)出一个AjaxLink来。    
  18.             ... //如果OnSuccess不是null,记得AjaxLink里边要放上OnSuccess。     
  19.             ... //关键代码:imglink(是个<A>tagbuilder).MergeAttribute("onclick", "Sys.Mvc.AsyncHyperlink.handleClick(this, new Sys.UI.DomEvent(event), { insertionMode: Sys.Mvc.InsertionMode.replace, updateTargetId: '" + ajaxUpdateTargetID + "', onSuccess: Function.createDelegate(this, " + ajaxOnSuccess + ") });");    
  20.             innerHtml += clickshowbody.ToString();   
  21.             return new MvcHtmlString(innerHtml);        }   
  22. </A>   

这样,函数里边就把放置Value的DIV(也就是日后会被更新的那个)、弹出菜单、弹出菜单上应该写的东西、点击后该干什么、弹出后该干什么等等都处理了,扔到哪里都工作。
下面是点击后弹出的效果:

更复杂的接口

上面的例子基本上解决了简单的列表式弹出窗口的情况,如果弹出来的东西比较复杂怎么办?比如我想把一个任务分配给我负责的组或我参与的组的某个组员,而且不希望这些组员直接按姓名排列(假设我记不住这些人),希望能呈现出一个下面这样的界面来:

图中点一下人名,就能把这个任务分配给这个人。

这个时候千万别先想技术实现手段,而是先要想一下:到底有几个业务信息要处理?其实就4个:

1. 哪个任务?任务的ID

2. 什么信息要改?“当前负责人”

3. 可选人员有哪些?“我的组(我负责管理的和我参与的)”

4. 设置好了要做点什么?“刷新一下屏幕”(主要是为了让更新后的内容接受一下Document.Ready里边的JS重新刷一下)

所以函数声明是:


 
 
  1. print?public static MvcHtmlString AjaxSDCValue(Item item, int sdcTypeValue, AjaxValue.ValuesKeys valuesKey, string ajaxOnSuccess = null)   
  2.  
  3. public static MvcHtmlString AjaxSDCValue(Item item, int sdcTypeValue, AjaxValue.ValuesKeys valuesKey, string ajaxOnSuccess = null

item是那个任务

sdcTypeValue是之前定义好的自定义字段的值(不是为了这个AjaxValue定义的,SDC=System Defined Column系统自定义字段)

AjaxValue.ValuesKey是个enum稍微复杂一点。之前总是直接输入string[]来代表未来的值,现在有可能是User[]……等等,所以干脆传递一个enum由函数内部判断这次是找什么,里边再详细处理。

urlFormat哪去了?由于AjaxSDCValue是我们专门用来处理系统自定义字段的,所以urlFormat是统一的,请看函数内部:


 
 
  1. print?public static MvcHtmlString AjaxSDCValue(Item item, int sdcTypeValue, AjaxValue.ValuesKeys valuesKey, string ajaxOnSuccess = null)   
  2. {   
  3.     string value = item.ReadSDCValueOf(sdcTypeValue);   
  4.     string urlFormat = "/SFC/Items/AjaxSetSDCValue?<itemID={0}&sdcTypeValue=" + SDCType.USER_CURRENT_OWNER+ "&value={0}";               
  5.  return AjaxValue(item.ID, value, valuesKey, urlFormat, ajaxOnSuccess);        }  

实际上完全使用原来的函数AjaxValue也能实现,但是向用户暴露了过多的技术细节(尤其是形成urlFormat的式子太复杂),完全是用户不应该关心的。

/SFC/items/AjaxSetSDCValue会把Item的USER_CURRENT_OWNER字段设置为value的值。

弹出的复杂菜单哪来的?我们总结了一下,发现AjaxValue大致可能弹出以下几种东西:

1. 最基本的一串字符串(原来那个string[],可以处理可用的预估值、剩余时间值、花费时间值……)

2. 我的组的人员(分配任务、分配Bug、安排值班、安排休假……)

3. ……

urlFormat不同点击后的功能就不同,但上述菜单的结构和外观每次都相同。

所以就做了一个AjaxController.PopupAjaxValuesMenu,会根据传进来的AjaxValue.ValuesKey这个enum来判断弹出哪个菜单(包括准备菜单中的数据),顺便把urlFormat也传进去。

回顾

设计和调试这一堆东西还是花了2天时间的,但是从此再也不用见到大约每次都要50行左右的C#/Html代码了。

当未来的维护者看到这段代码时:


 
 
  1. print?@SFCUI.AjaxSDCValue(Story, SDCType.USER_CURRENT_OWNER, AjaxValue.ValuesKeys.UsersMyTeams, ajaxOnSuccess: "function() { refreshStoryPad(" + Story.ID + "); }", imageUrl: "/SFC/Users/Details16.png")   

他会想:“哦,这里要把Story的CurrentOwner调整为……我的Team中的一个人,如果成功了会来刷新这条故事。而且,前面似乎还显示了个头像图标!”

这比面对50行漫天飞的代码要舒服多了。

 

写到最后,突然想起来10年前总结过的另一句话:

封装的最高境界,就是用和自然语言一样多的文字写完调用代码,剩下的全部隐匿起来。



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

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