这是松结对编程的第22篇(专栏目录)。

接前文

业务代码

比较长,基本上就是看被注释隔开的三大段,先显示状态群筛选链接,然后是单个状态筛选,然后是显示下拉框的当前选中项,最后显示下拉框。
  1. public static MvcHtmlString StatusFiltersDropdownList(WebViewPage page)
  2. {
  3. var allStatuses = Status.AllStatuses().ToList();
  4. const string key = "statusIds";
  5. var currentStatusIds = page.ParameterOf(key);
  6.  
  7. //Status groups filters on the top.
  8. var linksLeft = new Dictionary<string, IEnumerable<Status>>
  9. {
  10. {"所有状态", allStatuses},
  11. {"所有正常", allStatuses.Where(i => i.IsNormal)},
  12. {"所有开放", allStatuses.Where(i => !i.IsClosed)},
  13. {"所有故事板", allStatuses.Where(i => i.IsDisplayedOnKanban)},
  14. };
  15. var linksListLeft = AddToList(page, key, currentStatusIds, linksLeft);
  16.  
  17. var linksRight1 = new Dictionary<string, IEnumerable<Status>>
  18. {
  19. {"0", null},
  20. {"所有已放弃或推迟", allStatuses.Where(i => !i.IsNormal)},
  21. {"所有关闭", allStatuses.Where(i => i.IsClosed)},
  22. {"所有非故事板", allStatuses.Where(i => !i.IsDisplayedOnKanban)}
  23. };
  24. var linksListRight1 = AddToList(page, key, currentStatusIds, linksRight1);
  25.  
  26. //Single status filters on the bottom.
  27. linksListLeft.Add(new MvcHtmlString("<hr/>"));
  28. linksListLeft.AddRange(allStatuses.Where(i => i.Value >= 0)
  29. .Select(status => status.Link(outerLink: page.MergeParameter(key, "_" + status.ID + "_"), title: status.Value.ToString(),
  30. displayAsBoldText: "_" + status.ID + "_" == currentStatusIds)));
  31. linksListLeft.Add(new MvcHtmlString("<hr/>"));
  32. linksListLeft.AddRange(allStatuses.Where(i => i.Value < 0)
  33. .Select(status => status.Link(outerLink: page.MergeParameter(key, "_" + status.ID + "_"), title: status.Value.ToString(),
  34. displayAsBoldText: "_" + status.ID + "_" == currentStatusIds)));
  35.  
  36. //Current value text
  37. var currentValueText = "";
  38. MvcHtmlString currentValue = null;
  39. currentValueText = linksLeft.SingleOrDefault(i => i.Value != null && currentStatusIds == i.Value.Aggregate<Status, string>(null, (current, status) => current + "_" + status.ID + "_"))
  40. .Key ?? currentValueText;
  41. currentValueText = linksRight1.SingleOrDefault(i => i.Value != null && currentStatusIds == i.Value.Aggregate<Status, string>(null, (current, status) => current + "_" + status.ID + "_"))
  42. .Key ?? currentValueText;
  43. if (!string.IsNullOrEmpty(currentValueText))
  44. {
  45. currentValue = new MvcHtmlString("<b>" + currentValueText + "</b>");
  46. }
  47. else
  48. {
  49. var status = allStatuses.SingleOrDefault(i => currentStatusIds == "_" + i.ID + "_");
  50. currentValue = status != null
  51. ? MFCUI.Image(status.Title, "/MFC/Items/STAT/STAT16.png", showText: true, cssClassOfText: "bold", textColor: status.Color)
  52. : new MvcHtmlString("<b style=\"color: #169; \">请选择</b>");
  53. }
  54.  
  55. var ddl = MFCUI.DropdownListHtml(page, currentValue, linksListLeft, linksListRight1, null, "200px");
  56. return new MvcHtmlString("状态:" + ddl);
  57. }

有一个被调用的函数在这里:

基于上篇文章中提到的“业务代码设计原则”,有几个相关的点(和上篇中的顺序有差异):
1. 所有无关业务的东西都不在这里实现
比如MFCUI.ImageLink/Link, page.ParameterOf()/MergeParameter(从URL中取出或放入某个参数的数值),MFCUI.DropdownListhtml(显示下拉框)等,都与业务无关,只调用,不散开写。
这些函数都是公共使用的函数,应该早就写好,或这次写好留待后用。
2. 不写重复代码
某些函数入AddToList()是只在这个函数中调用的,没人会复用,但在这里本身被调用多次是重复代码,则写一个Private的函数放下面就可以了:
  1. private static List<MvcHtmlString> AddToList(WebViewPage page, string key, string currentStatusIds, Dictionary<string, IEnumerable<Status>> linkList)
  2. {
  3. var linksList = new List<MvcHtmlString>();
  4. foreach (var link in linkList)
  5. {
  6. var statusIds = link.Value == null ? "" : link.Value.Aggregate<Status, string>(null, (current, status) => current + "_" + status.ID + "_");
  7. linksList.Add(link.Value == null
  8. ? null
  9. : MFCUI.Link(link.Key, page.MergeParameter(key, statusIds), displayAsBoldText: statusIds == currentStatusIds));
  10. }
  11. return linksList;
  12. }

这样封装的代码没有复用价值,但可以减少维护时的阅读量。

3. 业务代码中只描述做什么,不描述怎么做。
乍一看这一大段代码,肯定不可能看懂“是怎么实现的”,其实,这就对了。
因为这样反而分离了要做什么和怎么做两块,党只关心要做什么(比如业务发生变更)时,就只看做什么的代码;想知道怎么做的时候再说。
下篇会通过一次“业务变更”来展示如何从代码分离中受益。

关于注释

读者可能注意到代码中注释很少,培训中也经常有人问到“注释应该占多大比例”的问题,这个问题要回答好还比较复杂。
注释的多少以能看懂代码能维护为准。要看懂代码,除了注释之外,命名、布局、函数封装等都有利于读懂代码,而在L型代码结构中,代码的使用次数是另外一个帮助复用、阅读、维护代码的方法。
火星人现在基本版的文件数只有622个(cs + cshtml),代码只有7500行(仅计算C#,由VS2012自带功能统计),而上面提到的MFCUI.ImageLink/Link一共被使用过280次/220次,parameterOf/MergeParameter 79次/62次,DropdownListHtml()33次。
在这种情况下,任何新手/新人想使用它们前,第一个想到的绝不是阅读注释或文档,而是先在界面上找一个自己想要的实例做示例,然后拷贝粘贴修改参数就可以了。最初的示例是由师傅写出来的(可复用的代码也是他写的),而之后大家不断参考使用,一点点就学会了。因此这些函数不但使用时很少有注释,函数接口定义处也几乎没有注释。
但是,若L型代码结构被复用的范围超出了示例代码的范围,情况就不同了。这时候就需要写点注释了。(所以说“多少注释才好”这个问题有答案,但比较复杂)
无论如何,更有参考价值的无疑是示例而非注释。

业务逻辑代码

刚才说的全是界面上的东西,底层怎么实现筛选的呢?
其实statusIds原封不动地传递了大约5层后到达这里:
  1. subItems = statusIds == null
  2. ? ItemsUnder(repository, rootID, includeHidden: includeHidden)
  3. : ItemsUnder(repository, rootID, includeHidden: includeHidden).Where(i => i.Status != null && statusIds.Contains("_" + i.Status.ID + "_"));

这是它唯一被真正使用的地方。尽管我们会对很多东西进行筛选,但都不需要写任何一行代码了,因为这行代码位于“很多东西”的底层。日后如果技术上有什么变动,就修改它就可以了。

实现业务变更

现在我们需要实现“晚于某个状态的所有工作项”筛选器(比如自动集成时,只运行“所有晚于故事板上已完成的故事,以及部署中、部署完毕的故事”的测试用例,这个是我们自己的实际需求,写本博客时还没做),下篇文章将通过增加这个新业务,来展示L型代码 结构如何使得只通过业务代码而无需阅读技术代码就进行维护变为可能的。
(写下篇文章前需要写一些代码,会晚0.5~1小时左右发布)

待续

敏捷开发松结对编程系列:L型代码结构案例StatusFiltersDropdownList(中)的更多相关文章

  1. L型代码结构案例:Link访问权限(上)

    这是松结对编程的第20篇(专栏目录). 本文探讨Link访问权限的最佳实现方法,力求外观干净且封装良好. 这些代码将位于L型代码结构(参见松结对编程系列中的定义)的下层,调用者无需理解其原理. 顺便说 ...

  2. shell编程系列26--大型脚本工具开发实战

    shell编程系列26--大型脚本工具开发实战 大型脚本工具开发实战 拆分脚本功能,抽象函数 .function get_all_group 返回进程组列表字符串 .function get_all_ ...

  3. shell编程系列21--文本处理三剑客之awk中数组的用法及模拟生产环境数据统计

    shell编程系列21--文本处理三剑客之awk中数组的用法及模拟生产环境数据统计 shell中的数组的用法: shell数组中的下标是从0开始的 array=("Allen" & ...

  4. shell编程系列19--文本处理三剑客之awk中的字符串函数

    shell编程系列19--文本处理三剑客之awk中的字符串函数 字符串函数对照表(上) 函数名 解释 函数返回值 length(str) 计算字符串长度 整数长度值 index(str1,str2) ...

  5. 《Windows核心编程系列》十三谈谈在应用程序中使用虚拟内存

    在应用程序中使用虚拟内存 Windows提供了以下三种机制对内存进行操控: 一:虚拟内存.最适合来管理大型对象数据或大型结构数组. 二:内存映射文件.最适合用来管理大型数据流,以及在同一机 器上运行的 ...

  6. 安卓APP开发简单实例 结对编程心得

    开始说起搞APP开发,自己和小伙伴的编程水平真的很低,无从下手,只有在网上找点案列,学习着怎样开发,结对编程还是面临着许多问题的,大家的水平有所差异和编程风格不同,我们用eclipse做了一个仿微信登 ...

  7. 敏捷开发与XP实践

    北京电子科技学院(BESTI) 实  验  报  告 课程: Java        班级:1352          姓名:黄伟业         学号:20135215 成绩:           ...

  8. 实验三 敏捷开发与XP实践

    实验内容 1. XP基础 2. XP核心实践 3. 相关工具 实验要求 1.没有Linux基础的同学建议先学习<Linux基础入门(新版)><Vim编辑器> 课程 2.完成实验 ...

  9. 20145215实验三 敏捷开发与XP实践

    20145215实验三 敏捷开发与XP实践 实验内容 XP基础 XP核心实践 相关工具 实验步骤 (一)敏捷开发与XP 软件工程是把系统的.有序的.可量化的方法应用到软件的开发.运营和维护上的过程.软 ...

随机推荐

  1. libctemplate——C语言模块引擎简介及使用

    前言 由先声明此libctemplate不是Google那个ctemplate.这个库是用C语言实现的,只有一个实现文件和一个头文件.Gooogl的ctemplate是C++实现的,和线程还扯上了关系 ...

  2. StandardServiceRegistryBuilder

    org.hibernate.boot.registry.StandardServiceRegistryBuilderhibernate4.3 Configuration cfg = new Confi ...

  3. CI源码引用使用--php引用demo,静态变量和引用关系

    CI源码引用使用在Common.php中,加载配置和类的方法 function &test() {     static $a = '';     if (!$a) {         $a ...

  4. Git关联远程GitHub仓库

    一.本地安装GIT版本控制软件 二.配置Git,设置用户信息 git config --global user.name "jack" git config --global us ...

  5. 实现Word的列表样式

    1.创建列表,但是不要求在文档视图中显示的层级列表 1)首先是要先把层级建立好,然后选中要编号文字.开始->段落->多级列表,选择一个列表样式,会默认所有的编号文字都是一级: 2)选择&q ...

  6. 2016最新Java笔试题集锦

    更新时间:2015-08-13         来源:网络         投诉删除 [看准网(Kanzhun.com)]笔试题目频道小编搜集的范文“2016最新Java笔试题集锦”,供大家阅读参考, ...

  7. hg vs git :这个世界除了svn还有别的

    最近想用版本控制软件来保存汉化文件,但又觉得SVN太麻烦,于是想到了最近较为流行的分布式版本控制工具.而Git和Mercurial(意思为水银的,于是经常缩写为Hg)自然是其中最为流行的工具.大名鼎鼎 ...

  8. 自己挖坑自己跳 之JsonMappingException: (was java.lang.NullPointerException) (through reference chain:)

    在Web项目中,我们经常会设计一些与界面相对应的JavaBean作为Entity,而为了兼容前台传入的空值,有些字段我们会用包装类型而不是基本类型.可是往往我的Entity已经设计完成,很多时候我们会 ...

  9. L1、L2范式及稀疏性约束

    L1.L2范式及稀疏性约束 假设需要求解的目标函数为: E(x) = f(x) + r(x) 其中f(x)为损失函数,用来评价模型训练损失,必须是任意的可微凸函数,r(x)为规范化约束因子,用来对模型 ...

  10. 深入了解一下PYTHON中关于SOCKETSERVER的模块-A

    有了这块知识,应该对各类WEB框架有更好的理解吧..FLASK,DJANGO,WEBPY.... #!/usr/bin/env python from BaseHTTPServer import HT ...