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

接前文

业务代码

比较长,基本上就是看被注释隔开的三大段,先显示状态群筛选链接,然后是单个状态筛选,然后是显示下拉框的当前选中项,最后显示下拉框。
        public static MvcHtmlString StatusFiltersDropdownList(WebViewPage page)
{
var allStatuses = Status.AllStatuses().ToList();
const string key = "statusIds";
var currentStatusIds = page.ParameterOf(key); //Status groups filters on the top.
var linksLeft = new Dictionary<string, IEnumerable<Status>>
{
{"所有状态", allStatuses},
{"所有正常", allStatuses.Where(i => i.IsNormal)},
{"所有开放", allStatuses.Where(i => !i.IsClosed)},
{"所有故事板", allStatuses.Where(i => i.IsDisplayedOnKanban)},
};
var linksListLeft = AddToList(page, key, currentStatusIds, linksLeft); var linksRight1 = new Dictionary<string, IEnumerable<Status>>
{
{"0", null},
{"所有已放弃或推迟", allStatuses.Where(i => !i.IsNormal)},
{"所有关闭", allStatuses.Where(i => i.IsClosed)},
{"所有非故事板", allStatuses.Where(i => !i.IsDisplayedOnKanban)}
};
var linksListRight1 = AddToList(page, key, currentStatusIds, linksRight1); //Single status filters on the bottom.
linksListLeft.Add(new MvcHtmlString("<hr/>"));
linksListLeft.AddRange(allStatuses.Where(i => i.Value >= 0)
.Select(status => status.Link(outerLink: page.MergeParameter(key, "_" + status.ID + "_"), title: status.Value.ToString(),
displayAsBoldText: "_" + status.ID + "_" == currentStatusIds)));
linksListLeft.Add(new MvcHtmlString("<hr/>"));
linksListLeft.AddRange(allStatuses.Where(i => i.Value < 0)
.Select(status => status.Link(outerLink: page.MergeParameter(key, "_" + status.ID + "_"), title: status.Value.ToString(),
displayAsBoldText: "_" + status.ID + "_" == currentStatusIds))); //Current value text
var currentValueText = "";
MvcHtmlString currentValue = null;
currentValueText = linksLeft.SingleOrDefault(i => i.Value != null && currentStatusIds == i.Value.Aggregate<Status, string>(null, (current, status) => current + "_" + status.ID + "_"))
.Key ?? currentValueText;
currentValueText = linksRight1.SingleOrDefault(i => i.Value != null && currentStatusIds == i.Value.Aggregate<Status, string>(null, (current, status) => current + "_" + status.ID + "_"))
.Key ?? currentValueText;
if (!string.IsNullOrEmpty(currentValueText))
{
currentValue = new MvcHtmlString("<b>" + currentValueText + "</b>");
}
else
{
var status = allStatuses.SingleOrDefault(i => currentStatusIds == "_" + i.ID + "_");
currentValue = status != null
? MFCUI.Image(status.Title, "/MFC/Items/STAT/STAT16.png", showText: true, cssClassOfText: "bold", textColor: status.Color)
: new MvcHtmlString("<b style=\"color: #169; \">请选择</b>");
} var ddl = MFCUI.DropdownListHtml(page, currentValue, linksListLeft, linksListRight1, null, "200px");
return new MvcHtmlString("状态:" + ddl);
}

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

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

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

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

关于注释

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

业务逻辑代码

刚才说的全是界面上的东西,底层怎么实现筛选的呢?
其实statusIds原封不动地传递了大约5层后到达这里:
            subItems = statusIds == null
? ItemsUnder(repository, rootID, includeHidden: includeHidden)
: 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. MVC5学习笔记

    买了一本MVC5的书:ASP.NET MVC 5 高级编程(第5版).边学边记录一下 1.快速创建模型类,如:自动实现的属性 {get;set;} 输入“prop",按Tab两次,默认属性值 ...

  2. JavaScript上下文和闭包

    "this" 上下文 上下文通常是取决于一个函数如何被调用.当函数作为对象的方法被调用时,this 被设置为调用方法的对象: var object = { foo: functio ...

  3. 【原创】Android 从一个Activity跳转到另外一个Activity

    Android四大组件activity使用,实现两个activity之间的跳转 基本流程:创建两个activity-将其中一个activity中组件作为事件源-通过组件事件的处理借助intent对象实 ...

  4. UML2.0统一建模语言

      Unified Modeling Language (UML)又称统一建模语言或标准建模语言,是支持模型化和软件系统开发的图形化语言,为软件开发的所有阶段提供模型化和可视化支持,包括由需求分析到规 ...

  5. Apache .htaccess Rewrite解决问号匹配的写法

    如news.asp?id=123 需要把它定向到 news/123.html 这个用 RewriteRule 怎么写啊? RewriteRule ^news\.asp\?id=(\d+)$ news/ ...

  6. io开发之C语言第二天

    开发环境是OS X系统下的Xcode Xcode的两个快捷键以及打开Xcode项目的正确方式 快捷键:command + B 编译 + 链接 快捷键:command + R 编译 + 链接 + 运行 ...

  7. linux下安装MySQL5.6记录

    把之前装的mysql卸载了,准备重新用代码包装一遍,问了一下公司的DBA,他推荐给我mysql-5.6.16版本,说这个版本比较稳定. 按照网上的教程安装,结果就少文件,我还在找原因的时候,同事来找我 ...

  8. bzoj 1004 1004: [HNOI2008]Cards burnside定理

    1004: [HNOI2008]Cards Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 1668  Solved: 978[Submit][Stat ...

  9. CTSC模拟题 树上的路径

    Description 给定一棵\(N\)个结点的树,结点用正整数\(1 \dots N\)编号,每条边有一个正整数权值.用\(d(a, b)\)表示从结点\(a\)到结点\(b\)路径上经过边的权值 ...

  10. Optimal Milking

    poj2112:http://poj.org/problem?id=2112 题意:K台挤奶机器,C头牛,K不超过30,C不超过200,每台挤奶机器最多可以为M台牛工作,给出这些牛和机器之间,牛和牛之 ...