惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
Tenable Blog
Last Week in AI
Last Week in AI
P
Proofpoint News Feed
Engineering at Meta
Engineering at Meta
H
Help Net Security
F
Fortinet All Blogs
MyScale Blog
MyScale Blog
宝玉的分享
宝玉的分享
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
博客园 - 司徒正美
量子位
N
Netflix TechBlog - Medium
Apple Machine Learning Research
Apple Machine Learning Research
小众软件
小众软件
Recorded Future
Recorded Future
博客园 - 三生石上(FineUI控件)
Vercel News
Vercel News
aimingoo的专栏
aimingoo的专栏
I
InfoQ
Microsoft Security Blog
Microsoft Security Blog
Scott Helme
Scott Helme
The Last Watchdog
The Last Watchdog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
IT之家
IT之家
AI
AI
WordPress大学
WordPress大学
Security Archives - TechRepublic
Security Archives - TechRepublic
Google Online Security Blog
Google Online Security Blog
U
Unit 42
V2EX - 技术
V2EX - 技术
MongoDB | Blog
MongoDB | Blog
Schneier on Security
Schneier on Security
博客园 - Franky
H
Heimdal Security Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Jina AI
Jina AI
W
WeLiveSecurity
P
Privacy & Cybersecurity Law Blog
Cloudbric
Cloudbric
B
Blog RSS Feed
N
News | PayPal Newsroom
S
Securelist
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
I
Intezer
Hacker News - Newest:
Hacker News - Newest: "LLM"
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
博客园_首页
罗磊的独立博客
H
Hackread – Cybersecurity News, Data Breaches, AI and More
雷峰网
雷峰网

博客园 - Gu-dong

人工智能与3A 敏捷中的自组织团队 历史总是螺旋上升的 一个成功敏捷团队的失败历程 敏捷书籍推荐列表 最近一个项目的一点反省:客户价值才是关注的焦点 Scrum vs. PMP vs. PRINCE2的发展趋势图 敏捷团队谁负责? 什么是产品的愿景—从一篇博文中学得到 全栈开发与敏捷 VS2005以后的MFC如何修改菜单的颜色 我对敏捷的理解:实施敏捷的前提 华为EC169 3G卡在Win7下的安装 推荐一个非常优秀的SharePoint Tag Cloud Webpart 如何在SharePoint Blog中显示附件(Attachement file) 王晓阳的搜狐博客被封了 也谈谈丰田事件 重建SharePoint Service 2.0的全过程 “用户体验”——微软Tech ED2009大会有感
Coding道场:第一次
Gu-dong · 2014-10-24 · via 博客园 - Gu-dong

10/23日,我在部门内部进行了一次内部学习,使用目前流行的Coding Dojo(道场)方式,进行了TDD开发的演练。演练的题目如下:

有关Coding道场的介绍,请自行百度一下,我就不再多做介绍了。

从效果来看,基本达到了传达TDD是什么样的开发方式的目的。尤其是大家从最初满脑子如何实现这个程序,怎样去设计算法,逐渐转变为了先想如何测试,从最简单的实现开始,最终演化成最终的设计。当然,目前为止,参加人员也只是理解了TDD是一个什么样的开发方式而已,还谈不到真正使用TDD进行开发。这需要一个更加长期的自我训练和使用的过程。使用TDD,最主要的是一种思维方式的变化。

首先:要坚信所有的程序皆可测,如果不能测试,不是产品的特性导致,而是自己的能力不足导致,设计上有问题。因此必须从设计上加以改变,使得程序可测。如一般认为曲线的显示是否正常,是无法使用自动测试的。换个角度:如果显示只是一个数据-坐标的转换的话,测试的重点就变成了数据是否正确,而这一点是完全可测的。

其次:虽然需要全局的考虑,但是要从简单入手,演进式设计。

这一点,在本次道场演练中体现的就很明显,此次道场开始,很多人的想法就是,建立某种算法,将需要的字符显示出来。于是第一个函数就是:void DisplaySegmentDigital(String input),然后再写那些子函数。如何测试这个函数?这是一个输出到屏幕显示的函数,它只能用眼来判断,显然不适合自动测试或者单元测试。所以,TDD不是一个先实现框架,再实现具体功能的做法。输出到屏幕,只是最后的一个过程,也是一个简单的过程,因此可以不必作为重点。重点在于显示的数据是什么?所以,函数就变为了:String DisplaySegmentDigital(String input)。这个时候,这个函数不再是向屏幕输出,而是输出一个字符串,再由另外一个字符串显示函数完成向屏幕的输出。而原来这个函数就变得可测了。于是,第一个测试函数被写出来了:

String strText = "910"; String strTextResult = "._.|_|..|.....|..|._.|.||_|";  

String strOutput = digitalSegment.DisplaySegmentDigital(strText);   

assertEquals(strOutput , strTextResult);

第一个测试顺利通过,因为实现非常简单:

public String DisplaySegmentDigital(String strText) {

    return "._.|_|..|.....|..|._.|.||_|";

}

接下来的困难是:下一个测试什么?测试“3456”的输出?OK,我们先试试看,于是我们想写第二个测试:

String strText = "3456"; String strTextResult = "????????";

问题接着出来了:这串问号该填什么?这样测试真的有意义么?几乎所有的人都直觉得发现这里有问题。简短的讨论后,结论是应该测试每个数字的显示,而非一个字符串。于是,测试变为:

String strText = "9"; String strTextResult = "._.|_|..|";  

String strOutput = digitalSegment.DisplaySegmentDigital(strText);  

assertEquals(strOutput , strTextResult);

实现变为:

public String DisplaySegmentDigital(String strText) {

 if(strText == "9")

  return "._.|_|..|";

 else

  return null;

}

实现后,接着测试:

strText = "1"; strTextResult = ".....|..|";  

strOutput = digitalSegment.DisplaySegmentDigital(strText);  

assertEquals(strOutput , strTextResult);

实现也变为:

public String DisplaySegmentDigital(String strText) {

 String[] strResult=new String[10];

 strResult[0]="._.|.||_|";

 strResult[1]=".....|..|";

 strResult[9]="._.|_|..|";

 return strResult[Integer.parseInt(strText)];

}

至此,很显然我们的算法也就自然而然的诞生了。可能与很多人自己开始的算法设计不太一样,但也不应该差到哪里:)。这就是TDD演进式设计。

但有个问题,._.|_|..|是什么东东?我怎么知道最终输出是正确的。因此,我们稍微改变了一下写法:

String strText = "9";   String strTextResult = "._." +
                                                                 "|_|" +
                                                                  "..|";     

String strOutput = digitalSegment.DisplaySegmentDigital(strText);

assertEquals(strOutput , strTextResult);

strText = "1";   

strTextResult = "..." +

                        "..|" +

                        "..|";     

strOutput = digitalSegment.DisplaySegmentDigital(strText);      

assertEquals(strOutput , strTextResult);

实现变为:

public String DisplaySegmentDigital(String strText) {

 String[] strResult=new String[10];

 strResult[0]="._." +

                     "|.|" +

                     "|_|";

 strResult[1]="..." +

                     "..|" +

                     "..|";

 strResult[9]="._." +

                    "|_|" +

                     "..|"; 

 return strResult[Integer.parseInt(strText)];

}

现在直观多了。

为什么一定要变得直观,其目的不单纯是为了程序的易读性,更重要的是:测试不应该抄实现的代码,实现也不要抄测试的代码,否则后果很严重。写测试代码时,必须是含着测试的心态,含着使用者的心态去写测试代码,而非一门心思去想实现。如果这样,TDD就失败了。这也是为什么TDD要求先写测试代码,再写实现代码的原因。因为我们一旦先想到了实现,那么接下来的测试,必然会跟着实现的逻辑走,从而违背“测试独立性”的原则。实现发生错误,测试也无法发现。

接下来需要整理一下代码,显然DisplaySegmentDigital这个函数名不是那么准确,后来议论了一番,得出的名字是:GetDigitalDisplayContent。结束后,我想GetDigitalFont可能更好。

好了,有关第一次道场就写到这里,留一个小小的问题:到目前为止我们还没有设计显示的算法。为了便于输出,目前的设计如何改进?