﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-零雨其蒙's Blog -文章分类-面向对象理论与实践</title><link>http://www.blogjava.net/sslaowan/category/19645.html</link><description>做优秀的程序员</description><language>zh-cn</language><lastBuildDate>Fri, 12 Aug 2011 09:57:51 GMT</lastBuildDate><pubDate>Fri, 12 Aug 2011 09:57:51 GMT</pubDate><ttl>60</ttl><item><title>The Design of design</title><link>http://www.blogjava.net/sslaowan/articles/355577.html</link><dc:creator>零雨其蒙</dc:creator><author>零雨其蒙</author><pubDate>Tue, 02 Aug 2011 05:28:00 GMT</pubDate><guid>http://www.blogjava.net/sslaowan/articles/355577.html</guid><wfw:comment>http://www.blogjava.net/sslaowan/comments/355577.html</wfw:comment><comments>http://www.blogjava.net/sslaowan/articles/355577.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sslaowan/comments/commentRss/355577.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sslaowan/services/trackbacks/355577.html</trackback:ping><description><![CDATA[<div><span style="font-family: Arial, sans-serif, Helvetica, Tahoma; font-size: 12px; line-height: 18px; "><h3><div><span style="font-size: 12px; font-weight: normal; "><strong style="font-weight: bold; ">楔子&nbsp;<br /></strong><br />&nbsp;&nbsp;&nbsp;&nbsp; 人月神话的作者Brooks新出了一本书叫做The Design of design，本文不是这本书的书评。写这一篇我已经想了很久了，内容不会太多，但是却很慎重。&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Design是个听起来特别高雅的词，很多人从事所谓的设计行业。比如建筑设计，机械设计，艺术设计，以及我所从事的软件设计。&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp; 英国戏剧家作家DorothySayers在《The Mind of Makr》里，将创造过程分为三个阶段，即构想（Idea），精神（Energy）（或实现（Implementation））以及交互（Interaction）。这代表着：&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp; 1）概念性构想的形成。&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp; 2）在真实的媒体中实现&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp; 3） 在真实的体验中与用户交互。&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp; 我特别赞同这一说法。这表明，设计最重要的是Idea。&nbsp;<br /><br />&nbsp;&nbsp;&nbsp; Brooks又讲述了莫扎特的故事，其父亲询问其歌剧进度，他回答：一切都谱好了，只是还没写下来而已。&nbsp;<br /><br />&nbsp;&nbsp;&nbsp; 我看过莫扎特传那部电影，因此我理解这段话会有所不同，在莫扎特头脑中的谱子实际上已经具有实现的部分，而不单单是Idea或者叫构思。因为认为会这样形容他快速而绝无修改的作曲过程，就好像把头脑中谱好的曲子誊写在纸上一样。&nbsp;<br /><br /><br /><br />&nbsp;&nbsp;&nbsp;<strong style="font-weight: bold; ">&nbsp;&nbsp; 正文</strong>&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1 设计是一项创造性活动，而实现则是另外一项活动。&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2 Design，常常会用一种Sign（符号）来表示Idea，Sign不是设计，而其表示的东西的才是设计。&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3 人们经常提及建筑行业，因为其设计和实现（建造）过程划分最为清晰。另外大型机械制造的设计也类似。&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4 设计过程与实现过程以及跟用户的交互的过程会循环多次，其他过程使设计变得有用，并且能够作为反馈促使设计改进。但是它们都不是设计。&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5 为了能够将设计和实现过程分离，需要有一种全面的展现形式交给实现人员，比如设计图纸，乐谱等。&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 6 对于设计软件而言，其实是有两个分类，一个是面向用户使用的软件设计，一个是软件自身结构的设计。项目经理，产品经理，销售主管等往往只看到第一种软件设计，而架构师往往才同时关注两者。&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp; 7 在软件设计中，必须承认源代码才是最终设计交付的形式，但是并不应该局限于C或者Java写出来文本文件才是源代码，如果编译系统能够直接将UML图编译成机器指令或者虚拟机指令，那么她们也是源代码。如果不能，其他的文档，图表只是设计的不同粒度，以及所设计的物品的不同部分的表现形式，他们代表设计，但不是全部。（这点与Jack W.Reeves的《What is Software Design》中的观点略有不同）&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp; 8 在设计软件时，程序员会不断地完成实现过程，然后看到软件运行时的效果，并与之交互，进行最基本的创造过程循环。这个过程最好要经常进行，快速进行。&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp; 9 对于艺术设计而言，比如设计一个Logo，想出这个Logo是什么样子，配色，以及其内涵是一个设计过程，而将其用Photoshop，AI或其他工具精确地绘制出来，只是一个施工过程。&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp; 10 由于不同的实现方式，对同样的设计实现难度不同，成本不同，因此好的设计师应该考虑这点。&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp; 11 因此好的设计师应该对实施工具了解。&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp; 12 精通PS，AI，或者Java，并不等同于好的设计师，因为好的设计在于你有怎样的想法。&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp; 13 对于软件设计而言，一种编程语言可能比另外一种更能表现设计意图。&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp; 14 另外对于软件设计而言，具体的一段微观的代码，或许不是软件设计，而是算法设计。不要一概而论。&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 15 由于有时所要设计的对象非常庞大，比如设计故宫，或者航空航天软件，那么需要考虑流程化设计过程，对设计进行管理。对设计元素进行组织。这有利于创造更好的设计，但不是设计本身。&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 16 一个优秀的设计师，并不一定精通你所认为那个设计工具，那个工具要么是建造工具，要么是为了进行设计交流，而不是设计本身。&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 17 同样，一个好的设计，并不在乎你使用了多么复杂的工具，利用了多少奇技淫巧，有时它很简单，比如Nike的标志。&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 18 设计跟任何其他的发明创造工作一样，需要继承和学习。&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 19 任何事情都有规律，做任何事情，都有成功模式和最佳实践。设计也一样。&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 20 我们应该广泛涉猎所有知识，以启发我们的设计。&nbsp;<br /><br /><br /><br />&nbsp;&nbsp;&nbsp; 设计是一种思想，而不是工具。&nbsp;<br /><br />&nbsp;&nbsp;&nbsp; 天赋，博学，以及表达，才能成为一个好的设计师。</span></div></h3></span></div><img src ="http://www.blogjava.net/sslaowan/aggbug/355577.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sslaowan/" target="_blank">零雨其蒙</a> 2011-08-02 13:28 <a href="http://www.blogjava.net/sslaowan/articles/355577.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>敏捷面向对象分析与设计思想 </title><link>http://www.blogjava.net/sslaowan/articles/211420.html</link><dc:creator>零雨其蒙</dc:creator><author>零雨其蒙</author><pubDate>Sat, 28 Jun 2008 13:14:00 GMT</pubDate><guid>http://www.blogjava.net/sslaowan/articles/211420.html</guid><wfw:comment>http://www.blogjava.net/sslaowan/comments/211420.html</wfw:comment><comments>http://www.blogjava.net/sslaowan/articles/211420.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sslaowan/comments/commentRss/211420.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sslaowan/services/trackbacks/211420.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Normal07.8 磅02falsefalsefalseMicrosoftInternetExplorer4/* Style Definitions */table.MsoNormalTable{mso-style-name:普通表格;mso-t...&nbsp;&nbsp;<a href='http://www.blogjava.net/sslaowan/articles/211420.html'>阅读全文</a><img src ="http://www.blogjava.net/sslaowan/aggbug/211420.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sslaowan/" target="_blank">零雨其蒙</a> 2008-06-28 21:14 <a href="http://www.blogjava.net/sslaowan/articles/211420.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>零雨其蒙：Practicing Test-Driven Development by Example Using Delphi</title><link>http://www.blogjava.net/sslaowan/articles/119830.html</link><dc:creator>零雨其蒙</dc:creator><author>零雨其蒙</author><pubDate>Thu, 24 May 2007 14:31:00 GMT</pubDate><guid>http://www.blogjava.net/sslaowan/articles/119830.html</guid><wfw:comment>http://www.blogjava.net/sslaowan/comments/119830.html</wfw:comment><comments>http://www.blogjava.net/sslaowan/articles/119830.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/sslaowan/comments/commentRss/119830.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sslaowan/services/trackbacks/119830.html</trackback:ping><description><![CDATA[&nbsp;
<p align="center"><strong><span>Practicing Test-Driven Development by Example Using <st1:place w:st="on">Delphi</st1:place></span></strong></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><strong><span>&nbsp;&nbsp;</span></strong></span><strong><span>零雨其蒙原创</span> </strong><strong><span>转载请注明</span> </strong></p>
<h1><span><span>1<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>测试驱动开发</span></h1>
<p><span>测试驱动开发不是什么噱头，而是真正有用的开发实践。今天派给我一个任务，让我解决一下退休提醒功能的</span><span>Bug</span><span>，我没看出原来的代码有何错误，不过觉得设计思路不十分的好：将数据库中所有的员工都取出来，然后再筛选应该被提醒的员工。而我觉得应该直接到数据库中去做筛选，返回大量无用的数据是资源的巨大浪费（我们在甲方公司里面开发，其网络之差令人发指）。</span></p>
<p><span>正好，我在研究</span><span>TDD</span><span>（本文有时指的是测试驱动开发，有时指的是测试驱动设计，因为两者都是存在的），想想何不来一次彻底的实践，真正的、完整的来一次</span><span>TDD</span><span>，体味一下其中的乐趣。</span></p>
<p><span>由于我们的开发工具是</span><span>Delphi</span><span>，因此自动测试工具自然而然就要使用</span><span>DUnit</span><span>了。尽管有的人说</span><span>TDD</span><span>不一定非得用自动测试框架，我也在使用</span><span>VB</span><span>进行</span><span>OO</span><span>系统开发时，用自制的测试程序进行测试，不过觉得那样都有一种不爽的感觉。因为总需要去维护复杂的测试代码，不能全力投入到测试驱动设计中。</span></p>
<h1><span><span>2<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>准备工作</span></h1>
<p><span>首先介绍一下业务：</span></p>
<p><span>很简单，就是在一个界面上显示即将退休的人员，具体提前多少天显示是从数据库中读取的参数。</span></p>
<p><span>然后配置</span><span>DUnit</span><span>环境，网上有</span><span>n</span><span>多教程，然后安装了一个</span><span>DUnit plug-in</span><span>插件，方便开发，网上也有讲解。</span></p>
<p><span>将</span><span>Stop on Delphi Exception</span><span>前的对号取消，这样就不会在出现异常时跳出了。</span></p>
<h1><span><span>3<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>开始</span><span>TDD</span><span>之旅</span></h1>
<p><span>本文是我进行</span><span>TDD</span><span>的实践记录，当然其间的思考要比这个多一些，不过主体部分基本都包含了，而且绝对写实。本文不是</span><span>TDD</span><span>的颂歌，我也提出了自己在实践中遇到的困难和疑惑。希望能给读者带来启示。</span></p>
<p><span>创建工程文件</span><span>HR.dpr</span><span>，然后使用</span><span>DUnit plug-in</span><span>，</span><span>New Project</span><span>，就自动在</span><span>HR.dpr</span><span>所在文件夹建了一个</span><span>dunit</span><span>文件夹，新建的测试工程默认名为</span><span>HRTests</span><span>，这是很好的规范，默认即可。然后</span><span>New TestModule</span><span>，建立一个测试单元。</span></p>
<p><span>接下来的工作就是在这两个同时开着的工程中开始工作了，一会我会切换到</span><span>HRTests</span><span>编写测试用例，一会我会在</span><span>HR</span><span>下编写产品代码，然后再回到</span><span>HRTests</span><span>下运行</span><span>Dunit</span><span>，进行测试。</span></p>
<h2><span><span>3.1<span> </span></span></span><span>领域驱动设计</span></h2>
<p><span><span>&nbsp;&nbsp; </span></span><span>首先构建领域层，领域概念就是退休（</span><span>Retired</span><span>），退休人员（</span><span>EmployeeRetired</span><span>）了。</span></p>
<p><span><span>&nbsp;&nbsp; </span></span><span>先创建这两个类，不少文章说先建立测试用例，然后测试时肯定显示红条，因为被测试的类还没有建立，我觉得没建立的话连编译都过不了，怎么运行</span><span>DUnit</span><span>啊？</span></p>
<p><span><span>&nbsp;&nbsp; </span></span><span>然后就可以开始根据想象编写测试用例了。思考对象的责任和工作方式，然后切换到产品工程添加这些责任。（有点像一边画顺序图一边画类图进行责任分配）</span><span>&nbsp;</span></p>
<p><span>首先，我创建类</span><span>TRetire</span></p>
<p><span>给</span><span>TRetire</span><span>类分配一个责任：查找退休提醒参数：</span></p>
<div>
<p><em><span>{ </span></em><em><span>作者：零雨其蒙</span></em><em></em></p>
<p><em><span>创建时间：</span></em><st1:chsdate w:st="on" year="2007" month="5" day="24" islunardate="False" isrocdate="False"><em><span>2007-5-24</span></em></st1:chsdate><em><span> </span></em></p>
<p><em><span>Blog:blog.csdn.net/sslaowan</span></em></p>
<p><em><span><span>&nbsp;&nbsp;&nbsp; </span>www.blogjava.net/sslaowan</span></em></p>
<p><em><span>}</span></em></p>
<p>&nbsp;</p>
<p><strong><span>function</span></strong><span> TRetire.getretireAwokeParaList: TObjectList;</span></p>
<p><span>var paraList:TObjectList;</span></p>
<p><strong><span>begin</span></strong></p>
<p><span>&nbsp;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span></span></p>
<p><strong><span>end</span></strong><span>;</span></p>
</div>
<p><span><span>&nbsp;&nbsp; </span></span><span>这是一个空壳，没有实际的内容，然后切换到</span><span>HRTest</span><span>工程，会出现下面的对话框。</span></p>
<p>&nbsp;&nbsp; <img height="128" alt="" src="http://lh3.google.com/image/sslaowan/RlWiKI2BsvI/AAAAAAAAAB0/lClAkZBW2ms/E%3A%5Cwanxing%5C%E7%A0%94%E7%A9%B6%5CTDD%5Creload.JPG" width="435" border="0" /></p>
<p><span><span>&nbsp;&nbsp; </span></span><span>在</span><span>HR</span><span>工程做了任何改动，保存后，都会在</span><span>HRTest</span><span>中有提醒。</span></p>
<p><span><span>&nbsp;&nbsp; </span></span><span>可能很多人从来没见过测试用例长什么样子，下面就给出一个完整的例子。</span></p>
<div>
<p><em><span>{ </span></em><em><span>作者：零雨其蒙</span></em><em></em></p>
<p><em><span>创建时间：</span></em><st1:chsdate w:st="on" year="2007" month="5" day="24" islunardate="False" isrocdate="False"><em><span>2007-5-24</span></em></st1:chsdate><em><span> </span></em></p>
<p><em><span>Blog:blog.csdn.net/sslaowan</span></em></p>
<p><em><span><span>&nbsp;&nbsp;&nbsp; </span>www.blogjava.net/sslaowan</span></em></p>
<p><em><span>}</span></em></p>
<p>&nbsp;</p>
<p><span>unit HRTestsTests;</span></p>
<p>&nbsp;</p>
<p><span>interface</span></p>
<p>&nbsp;</p>
<p><span>uses</span></p>
<p><span>&nbsp;TestFrameWork,</span></p>
<p><span>&nbsp;URetire,</span></p>
<p><span>&nbsp;Contnrs;</span></p>
<p>&nbsp;</p>
<p><span>type</span></p>
<p><span><span>&nbsp;&nbsp; </span>TTestRetire=class(TTestCase)</span></p>
<p><span><span>&nbsp;&nbsp; </span>private</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>retire:TRetire;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>retirePara:TRetirePara;</span></p>
<p><span><span>&nbsp;&nbsp; </span>protected</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>procedure SetUp; override;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>procedure TearDown; override;</span></p>
<p><span><span>&nbsp;&nbsp; </span>published</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>procedure testGetretireAwokeParaList;</span></p>
<p><span><span>&nbsp;&nbsp; </span>end;</span></p>
<p>&nbsp;</p>
<p><span>implementation</span></p>
<p>&nbsp;</p>
<p><span>function UnitTests: ITestSuite;</span></p>
<p><span>var</span></p>
<p><span>&nbsp;ATestSuite: TTestSuite;</span></p>
<p><span>begin</span></p>
<p><span>&nbsp;ATestSuite := TTestSuite.Create('Retire tests');</span></p>
<p><span>&nbsp;ATestSuite.AddTests(TTestRetire);</span></p>
<p><span>&nbsp;Result := ATestSuite;</span></p>
<p><span>end;</span></p>
<p>&nbsp;</p>
<p><span>{ TTestRetire }</span></p>
<p>&nbsp;</p>
<p><span>procedure TTestRetire.SetUp;</span></p>
<p><span>begin</span></p>
<p><span>&nbsp;inherited;</span></p>
<p><span><span>&nbsp;&nbsp; </span>retire:=TRetire.Create;</span></p>
<p><span><span>&nbsp;&nbsp; </span>retirePara:=TRetirePara.Create;</span></p>
<p><span>end;</span></p>
<p>&nbsp;</p>
<p><span>procedure TTestRetire.TearDown;</span></p>
<p><span>begin</span></p>
<p><span>&nbsp;inherited;</span></p>
<p><span>&nbsp;retire.Free;</span></p>
<p><span>&nbsp;retirePara.Free;</span></p>
<p><span>end;</span></p>
<p>&nbsp;</p>
<p><span>procedure TTestRetire.testGetretireAwokeParaList;</span></p>
<p><span>var paraList:TObjectList;</span></p>
<p><span>begin</span></p>
<p><span><span>&nbsp;&nbsp; </span>paraList:=retire.getretireAwokeParaList;</span></p>
<p><span><span>&nbsp;&nbsp; </span>retirePara:=TRetirePara(paraList.Items[0]);</span></p>
<p><span><span>&nbsp;&nbsp; </span>check(retirePara._emp_type='</span><span>管理人</span><span>员</span><span>');</span></p>
<p><span><span>&nbsp;&nbsp; </span>check(retirePara._sex='</span><span>男<span>');</span></span></p>
<p><span><span>&nbsp;&nbsp; </span>check(retirePara._retireage='60');</span></p>
<p><span><span>&nbsp;&nbsp; </span>check(retirePara._uptime='90');</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp; </span>retirePara:=TRetirePara(paraList.Items[1]);</span></p>
<p><span><span>&nbsp;&nbsp; </span>check(retirePara._emp_type='</span><span>管理人</span><span>员</span><span>');</span></p>
<p><span><span>&nbsp;&nbsp; </span>check(retirePara._sex='</span><span>女<span>');</span></span></p>
<p><span><span>&nbsp;&nbsp; </span>check(retirePara._retireage='55');</span></p>
<p><span><span>&nbsp;&nbsp; </span>check(retirePara._uptime='90');</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp; </span>retirePara:=TRetirePara(paraList.Items[2]);</span></p>
<p><span><span>&nbsp;&nbsp; </span>check(retirePara._emp_type='</span><span>工人<span>');</span></span></p>
<p><span><span>&nbsp;&nbsp; </span>check(retirePara._sex='</span><span>男<span>');</span></span></p>
<p><span><span>&nbsp;&nbsp; </span>check(retirePara._retireage='60');</span></p>
<p><span><span>&nbsp;&nbsp; </span>check(retirePara._uptime='90');</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp; </span>retirePara:=TRetirePara(paraList.Items[3]);</span></p>
<p><span><span>&nbsp;&nbsp; </span>check(retirePara._emp_type='</span><span>工人<span>');</span></span></p>
<p><span><span>&nbsp;&nbsp; </span>check(retirePara._sex='</span><span>女<span>');</span></span></p>
<p><span><span>&nbsp;&nbsp; </span>check(retirePara._retireage='50');</span></p>
<p><span><span>&nbsp;&nbsp; </span>check(retirePara._uptime='90');</span></p>
<p><span>end;</span></p>
<p>&nbsp;</p>
<p><span>initialization</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span>RegisterTest('Retire test',UnitTests);</span></p>
<p>&nbsp;</p>
<p><span>end.</span></p>
</div>
<p><span>在编写测试用例时，我发现返回的不是一个一维的表，而是二维的，我还是使用了对象</span><span>来报存另一维的数据，又创建了一个名为</span><span>TRetirePara</span><span>的类。其属性如上面的代码所示。</span></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; <img height="437" alt="" src="http://lh6.google.com/image/sslaowan/RlWiJ42BssI/AAAAAAAAABc/3PhrWsmbDZM/E%3A%5Cwanxing%5C%E7%A0%94%E7%A9%B6%5CTDD%5C1.JPG?imgmax=512" width="512" border="0" /></p>
<p><span>然后编译运行</span><span>HRTest</span><span>，出现</span><span>DUnit</span><span>，点击绿色的</span><span>RUN</span><span>按钮，出现红条。错误是</span><span>EAccessViolation</span><span>。</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp; </span></span><span>这是因为没有创建</span><span>TObjectlist</span><span>的实例</span><span>paraList</span><span>，而在</span><span>testGetretireAwokeParaList</span><span>中访问了它，这时切换到</span><span>HR</span><span>工程，在</span><span>getretireAwokeParaList</span><span>中添加如下代码。</span></p>
<div>
<p><em><span>{ </span></em><em><span>作者：零雨其蒙</span></em><em></em></p>
<p><em><span>创建时间：</span></em><st1:chsdate w:st="on" year="2007" month="5" day="24" islunardate="False" isrocdate="False"><em><span>2007-5-24</span></em></st1:chsdate><em><span> </span></em></p>
<p><em><span>Blog:blog.csdn.net/sslaowan</span></em></p>
<p><em><span><span>&nbsp;&nbsp;&nbsp; </span>www.blogjava.net/sslaowan</span></em></p>
<p><em><span>}</span></em></p>
<p><strong>&nbsp;</strong></p>
<p><strong><span>function</span></strong><span> TRetire.getretireAwokeParaList: TObjectList;</span></p>
<p><strong><span>var</span></strong><span> paraList:TObjectList;</span></p>
<p><strong><span>begin</span></strong></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>paraList:=TObjectList.Create;</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>getretireAwokeParaList:=paraList;</span></p>
<p>&nbsp;</p>
<p><strong><span>end</span></strong><span>;</span></p>
</div>
<p><span><span>&nbsp;&nbsp; </span></span><span>再次测试。</span></p>
<p>&nbsp;<img height="438" alt="" src="http://lh6.google.com/image/sslaowan/RlWiJ42BstI/AAAAAAAAABk/4Xi9YrN5Ya0/E%3A%5Cwanxing%5C%E7%A0%94%E7%A9%B6%5CTDD%5C2.JPG?imgmax=512" width="512" border="0" /></p>
<p><span>依然显示红色，错误是</span><span>List index out of bounds</span><span>，这是因为在</span><span>getretireAwokeParaList</span><span>方法中并没有向</span><span>paraList</span><span>中添加任何对象，这时需要再对</span><span>getretireAwokeParaList</span><span>进行重构。</span></p>
<p>&nbsp;</p>
<h2><span><span>3.2<span> </span></span></span><span>步伐到底要多小？</span></h2>
<p><span>当有了&#8220;测试驱动依赖症&#8221;后，就想让</span><span>DUnit</span><span>帮我思考一些内容，比如下一步应该编写什么。通过测试，我不断的清楚了自己下一步的任务。但是或许不需要如此小步的前进，如果已经有了很好的思路，可以一下子把刚才的程序都编完，甚至把整个</span><span>getretireAwokeParaList</span><span>方法都完成。</span></p>
<p><span>然而由于没有太多的</span><span>plan</span><span>，想一点编一点的，难免就会采取这样小的步伐，其实这样也很好，因为不至于写了一大堆，错了都不知道哪一句是罪魁祸首。经常看到有人在面对一堆不知道在哪个地方出错的代码时，采用了删除所有，然后一句一句还原，发现到哪句错误就改哪句。这种做法一般都被用于没有调试器的环境，比如</span><span>HTML</span><span>页面。如果有了调试器，传统的做法当然是设置断点，然后利用调试器进行跟踪。关掉调试器，以测试代替调试的一个支撑点是，不大可能会出现大段的需要你去跟踪错误的代码，因为很小的一步重构进行之后，就开始测试了，哪里有错误一目了然。当然调试器的作用并不只是跟踪某个变量在运行时的值的变化，还有理解代码在汇编一级上是如何工作的，这将更加有利于你调错。但是，总而言之，调试器肯定是帮助你调试错误的，小步前进的单元测试可以帮助你在不使用调试器的情况下，找出错误。</span></p>
<h2><span><span>3.3<span> </span></span></span><span>混入持久层的测试</span></h2>
<p><span>或许看到这篇文章的您，有更好的方法来完成这样的测试，请您告诉我，因为我也是使用</span><span>Dunit</span><span>进行</span><span>TDD</span><span>的新手，希望分享您的宝贵经验。</span></p>
<p><span>我们使用的是</span><span>ODAC</span><span>控件连接</span><span>ORACLE</span><span>数据库。在产品代码（</span><span>HR.dpr</span><span>）中，通常我们都是直接加载数据模块中</span><span>TOraSession</span><span>，来连接数据库，再用</span><span>TOraQuery</span><span>与之相连。</span><span>getretireAwokeParaList</span><span>的主要操作是从数据库中获取记录，产品代码如下：</span></p>
<div>
<p><em><span>{ </span></em><em><span>作者：零雨其蒙</span></em><em></em></p>
<p><em><span>创建时间：</span></em><st1:chsdate w:st="on" year="2007" month="5" day="24" islunardate="False" isrocdate="False"><em><span>2007-5-24</span></em></st1:chsdate><em><span> </span></em></p>
<p><em><span>Blog:blog.csdn.net/sslaowan</span></em></p>
<p><em><span><span>&nbsp;&nbsp;&nbsp; </span>www.blogjava.net/sslaowan</span></em></p>
<p><em><span>}</span></em></p>
<p><strong>&nbsp;</strong></p>
<p><strong><span>function</span></strong><span> TRetire.getretireAwokeParaList: TObjectList;</span></p>
<p><strong><span>var</span></strong><span> paraList:TObjectList;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>retirePara:TRetirePara;</span></p>
<p><strong><span>begin</span></strong></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>paraList:=TObjectList.Create;</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>_qry.Close;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>_qry.SQL.Clear;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>_qry.SQL.Text:='select * from HR1_RETIREPARAMETER';</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>_qry.Open;</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span><strong>if</strong> _qry.RecordCount&gt;0 <strong>then</strong></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span><strong>begin</strong></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>_qry.First;</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong>while</strong> <strong>not</strong> _qry.Eof&nbsp;<strong>do</strong></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong>begin</strong></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>retirePara:=TRetirePara.Create; </span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>retirePara._emp_type:=_qry.FieldByName('EMPLOYEE_TYPE').AsString;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>retirePara._sex:=_qry.FieldByName('SEX').AsString;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>retirePara._retireage:=_qry.FieldByName('RETIRE_AGE').AsString;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp;</span>retirePara._uptime:=_qry.FieldByName('UPTIME_DAYS').AsString;</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>paraList.Add(retirePara) ;</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>_qry.Next;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong>end</strong>;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span><strong>end</strong>;</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>getretireAwokeParaList:=paraList;</span></p>
<p>&nbsp;</p>
<p><strong><span>end</span></strong><span>;</span></p>
</div>
<p><span><span>&nbsp;&nbsp; </span></span><span>这比你在上一节看到的代码又丰富了许多。</span><span>_qry</span><span>是用到的</span><span>TOraQuery</span><span>类型的变量，它接受</span><span>TOraQuery</span><span>实例。由于进行单元测试时，</span><span>HR.dpr</span><span>是不启动的，因此</span><span>DM</span><span>根本就不会被创建。</span></p>
<p><span><span>&nbsp;&nbsp; </span></span><span>考虑再三，我在测试项目</span><span>HRTest.dpr</span><span>中加入了数据库连接代码，并将创建的</span><span>TOraQuery</span><span>实例赋值给</span><span>TRetire</span><span>的属性（</span><span>property</span><span>）</span><span>qry</span><span>。代码如下：</span></p>
<div>
<p><em><span>{ </span></em><em><span>作者：零雨其蒙</span></em><em></em></p>
<p><em><span>创建时间：</span></em><st1:chsdate w:st="on" year="2007" month="5" day="24" islunardate="False" isrocdate="False"><em><span>2007-5-24</span></em></st1:chsdate><em><span> </span></em></p>
<p><em><span>Blog:blog.csdn.net/sslaowan</span></em></p>
<p><em><span><span>&nbsp;&nbsp;&nbsp; </span>www.blogjava.net/sslaowan</span></em></p>
<p><em><span>}</span></em></p>
<p><strong>&nbsp;</strong></p>
<p><strong><span>procedure</span></strong><span> TTestRetire.SetUp;</span></p>
<p><strong><span>begin</span></strong></p>
<p><span>&nbsp;<strong>inherited</strong>;</span></p>
<p><span><span>&nbsp;&nbsp; </span>_qry:=TOraQuery.Create(nil);</span></p>
<p><span>&nbsp;<em><span>{ _session:=TOraSession.Create(nil);</span></em></span></p>
<p><em><span><span>&nbsp;&nbsp; </span>_session.Server:=';</span></em></p>
<p><em><span><span>&nbsp;&nbsp; </span>_session.ConnectString:='';</span></em></p>
<p><em><span><span>&nbsp;&nbsp; </span>_session.Username:='';</span></em></p>
<p><em><span><span>&nbsp;&nbsp; </span>_session.Password:='';</span></em></p>
<p><em><span><span>&nbsp;&nbsp; </span>_session.ConnectPrompt:=false;</span></em></p>
<p><em><span><span>&nbsp;&nbsp; </span>_session.Connect;&nbsp;}</span></em></p>
<p><span><span>&nbsp;&nbsp; </span>_dmhr:=TDMHR.Create(nil);</span></p>
<p><span><span>&nbsp;&nbsp; </span>_session:=TOraSession.Create(nil);</span></p>
<p><span><span>&nbsp;&nbsp; </span>_session:= _dmhr.HRSession ;</span></p>
<p><span><span>&nbsp;&nbsp; </span>_qry.Session:=_session;</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp; </span>retire:=TRetire.Create;</span></p>
<p><span><span>&nbsp;&nbsp; </span>retire</span><span>.qry:=</span><span>_qry;</span></p>
<p><strong><span>end</span></strong><span>;</span></p>
</div>
<p><span>起初，我创建了<span>TOraSession</span>对象，可是不知道为什么说驱动器有错误，于是就创建了一个<span>DM</span>（数据模块），然后获得其中的<span>HRSession</span>。注释掉的代码有何错误还请高人指点。之后我又写了个测试连接是否成功的方法。</span></p>
<div>
<p><em><span>{ </span></em><em><span>作者：零雨其蒙</span></em><em></em></p>
<p><em><span>创建时间：</span></em><st1:chsdate w:st="on" year="2007" month="5" day="24" islunardate="False" isrocdate="False"><em><span>2007-5-24</span></em></st1:chsdate><em><span> </span></em></p>
<p><em><span>Blog:blog.csdn.net/sslaowan</span></em></p>
<p><em><span><span>&nbsp;&nbsp;&nbsp; </span>www.blogjava.net/sslaowan</span></em></p>
<p><em><span>}</span></em></p>
<p><strong>&nbsp;</strong></p>
<p><strong><span>procedure</span></strong><span> TTestRetire.testConnect;</span></p>
<p><strong><span>begin</span></strong></p>
<p><span><span>&nbsp;&nbsp; </span>check(_session.Connected=true);</span></p>
<p><strong><span>end</span></strong><span>;</span></p>
</div>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp; </span></span><span>之后再进行测试，绿条终于出现了！</span></p>
<p>&nbsp;<img height="437" alt="" src="http://lh3.google.com/image/sslaowan/RlWiKI2BsuI/AAAAAAAAABs/sgtmXZpdcxI/E%3A%5Cwanxing%5C%E7%A0%94%E7%A9%B6%5CTDD%5C3.JPG?imgmax=512" width="512" border="0" /></p>
<p><span>我在之前还犯了错误，总是出现红条。后来才发现原来对象创建没搞清楚。这个错误在我编写</span><span>Java</span><span>程序时也犯过，看来一定要注意阿。</span></p>
<div>
<p><em><span>{ </span></em><em><span>作者：零雨其蒙</span></em><em></em></p>
<p><em><span>创建时间：</span></em><st1:chsdate w:st="on" year="2007" month="5" day="24" islunardate="False" isrocdate="False"><em><span>2007-5-24</span></em></st1:chsdate><em><span> </span></em></p>
<p><em><span>Blog:blog.csdn.net/sslaowan</span></em></p>
<p><em><span><span>&nbsp;&nbsp;&nbsp; </span>www.blogjava.net/sslaowan</span></em></p>
<p><em><span>}</span></em></p>
<p><strong>&nbsp;</strong></p>
<p><strong><span>while</span></strong><span> <strong>not</strong> _qry.Eof&nbsp;<strong>do</strong></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong>begin</strong></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>retirePara:=TRetirePara.Create; </span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>retirePara._emp_type:=_qry.FieldByName('EMPLOYEE_TYPE').AsString;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span><span>&nbsp;&nbsp;</span></span><em><span>&nbsp;</span></em><em><span>//</span></em><em><span>省略若干行</span></em><em></em></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>paraList.Add(retirePara) ;</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>_qry.Next;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong>end</strong>;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span><strong>end</strong>;</span></p>
</div>
<p><span>原来将</span><span>retirePara:=TRetirePara.Create;</span><span>这句写在循环之外了，结果测试时，只有最后一条（<span>paraList.Item[3]</span>对应的结果）是正确的。这个错误大家一看就知道了，每循环一次都需要创建一个<span>retirePara</span>对象，要不然向<span>paraList</span>添加的其实都是一个对象，而<span>paraList</span>的每个元素都是指向同一个对象引用，赋值之后，当然每个对象的属性值都是一样的啦。</span></p>
<p><span>还有，在测试用例中进行比较时，我刚开始图省事，都使用<span>check</span>，结果错了后没有任何提示，后来到<span>TestFramework</span>中查到了<span>CheckEquals</span>方法，这个方法很好，如果出错了，会告诉你期望值是什么，实际值是什么。</span></p>
<h2><span><span>3.4<span> </span></span></span><span>进化式设计</span></h2>
<p><span>TDD</span><span>韵律操是：编写单元测试——〉测试，红条——〉编写产品代码——〉绿条——〉编写单元测试——〉测试，红条——〉编写产品代码——〉绿条，编写产品代码并不能总是一气呵成，因此就会在编写部分产品代码——〉绿条——〉重构——〉测试，绿条</span><span>/</span><span>红条——〉重构——〉测试，绿条</span><span>/</span><span>红条</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span></span><span>下面开始编写</span><span>function retireAwoke(qry:TOraQuery):TObjectList;</span><span>方法。还是先写测试用例。</span><span><span>&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>这里面有个问题，这个方法的作用是查询并返回所有的符合退休条件的员工，每天的人可能都不一样，那么该怎样写测试用例呢？这个或许应该从<span>DUnit</span>的测试装备中读取（如果<span>DUnit</span>有的话，我也不知道有没有），也可以从一个文本文件或者<span>Excel</span>中读取，这样或许好些。但是这依然不是一个可回归测试。</span></p>
<p><span>最终想的办法是通过</span><span>SQL</span><span>语句先查询一下，看看有哪些记录，而且这个</span><span>SQL</span><span>语句与程序中的不尽相同。主要是将计算退休者生日的程序写在了</span><span>SQL</span><span>中还是写在程序中的区别。</span></p>
<p><span>使用如下的</span><span>SQL</span><span>语句进行查询：</span></p>
<div>
<p><span>select</span><span> employeeid,employeename,dptname,birthday,sex,EMPLOYEETYPE</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span><span>from</span> HR1_EMPLOYEE <span>left join</span> hr1_workdept <span>on</span> hr1_employee.workdeptid=hr1_workdept.dptid</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span><span>where</span> sex=<span>'</span></span><span>男</span><span>'</span><span> <span>and</span> EMPLOYEETYPE=<span>'</span></span><span>管理人员</span><span>'</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span><span>and</span> BIRTHDAY between <span>'<st1:chsdate w:st="on" year="1947" month="5" day="24" islunardate="False" isrocdate="False">1947-05-24</st1:chsdate>' and</span> (<span>select</span> to_char(to_date(<span>'<st1:chsdate w:st="on" year="1947" month="5" day="24" islunardate="False" isrocdate="False">1947-05-24</st1:chsdate>'</span>,<span>'yyyy-mm-dd'</span>)&nbsp;+ <span>interval</span> <span>'90'</span> <span>day</span>,<span>'yyyy-mm-dd'</span>)</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span><span>from</span> dual) <span>order by</span> dptid <span>asc</span>;</span></p>
</div>
<p><span>用于返回</span><span>60</span><span>岁退休的男性管理人员（提前</span><span>90</span><span>天提醒）。以下是</span><span>SQL Plus</span><span>的查询结果。</span></p>
<div>
<p><span>EMPLOYEEID EMPLOYEENA DPTNAME&nbsp;BIRTHDAY<span>&nbsp;&nbsp; </span>SE EMPLOYEETY</span></p>
<p><span>---------- ---------- ------------------- ---------- -- ----------</span></p>
<p><span>YG000043<span>&nbsp;&nbsp;&nbsp;张三丰</span></span><span><span>&nbsp;&nbsp;&nbsp; </span></span><span>人力资源部</span><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><st1:chsdate w:st="on" year="1947" month="7" day="17" islunardate="False" isrocdate="False">1947-04-16</st1:chsdate> </span><span>男</span><span> </span><span>管理人员</span></p>
<p><span>已选择</span><span> 1 </span><span>行</span></p>
</div>
<p><span>还需要说明的是，有四种情况需要测试，但是为了快速实现，我只是写了其中一种情况，即男，管理人员。然后我就写了测试程序。</span></p>
<div>
<p><em><span>{ </span></em><em><span>作者：零雨其蒙</span></em><em></em></p>
<p><em><span>创建时间：</span></em><st1:chsdate w:st="on" year="2007" month="5" day="24" islunardate="False" isrocdate="False"><em><span>2007-5-24</span></em></st1:chsdate><em><span> </span></em></p>
<p><em><span>Blog:blog.csdn.net/sslaowan</span></em></p>
<p><em><span><span>&nbsp;&nbsp;&nbsp; </span>www.blogjava.net/sslaowan</span></em></p>
<p><em><span>}</span></em></p>
<p><strong><span>procedure</span></strong><span> TTestRetire.testRetireAwoke;</span></p>
<p><strong><span>var</span></strong><span> employeeRetiredList:TObjectList;</span></p>
<p><span><span>&nbsp;&nbsp; </span></span><span><span>&nbsp;&nbsp;&nbsp;&nbsp;</span></span><span>&nbsp;i:integer;</span></p>
<p><strong><span>begin</span></strong></p>
<p><span>&nbsp;</span><span><span>&nbsp;&nbsp;&nbsp;&nbsp;</span></span><span>&nbsp;employeeRetiredList:=TObjectList.Create;</span></p>
<p><span><span>&nbsp;&nbsp; </span></span><span><span>&nbsp;&nbsp;&nbsp;&nbsp;</span></span><span>_employeeRetired:=TEmployeeRetired(employeeRetiredList.Items[0]);</span></p>
<p><span>&nbsp;</span><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></span><span>CheckEquals('</span><span> </span><span>YG00004</span><span>3</span><span>',_employeeRetired</span><span>._ID</span><span>);</span></p>
<p><span>CheckEquals('</span><span>张三丰</span><span>',_employeeRetired</span><span>._Name</span><span>);</span></p>
<p><strong><span>end;</span></strong></p>
</div>
<p><span>产品代码只是读出了参数列表的第一种情况。然后嵌套进</span><span>SQL</span><span>语句中进行查询。</span></p>
<div>
<p><em><span>{ </span></em><em><span>作者：零雨其蒙</span></em><em></em></p>
<p><em><span>创建时间：</span></em><st1:chsdate w:st="on" year="2007" month="5" day="24" islunardate="False" isrocdate="False"><em><span>2007-5-24</span></em></st1:chsdate><em><span> </span></em></p>
<p><em><span>Blog:blog.csdn.net/sslaowan</span></em></p>
<p><em><span><span>&nbsp;&nbsp;&nbsp; </span>www.blogjava.net/sslaowan</span></em></p>
<p><em><span>}</span></em></p>
<p><strong>&nbsp;</strong></p>
<p><strong><span>function</span></strong><span> TRetire.retireAwoke(qry:TOraQuery): TObjectList;</span></p>
<p><strong><span>var</span></strong><span> employeeRetired:TEmployeeRetired;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>paraList,employeesRetired:TObjectList;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>retirePara:TRetirePara;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>strSQL,strBirthdayUp, strBirthdayDown:string;</span></p>
<p><strong><span>begin</span></strong></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>_qry:= qry;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>employeesRetired:=TObjectList.Create;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>paraList:=getretireAwokeParaList;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>retirePara:=TRetirePara(paraList.Items[0]);<span>&nbsp;&nbsp; </span></span></p>
<p><em><span><span>&nbsp;&nbsp;&nbsp; </span>//</span></em><em><span>满足条件的男管理人员</span></em><em></em></p>
<p><span>&nbsp;dtBirthday:=EncodeDate(Yearof(date)-StrToInt(retirePara._retireage),monthof(date),DayOf(date));</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>strBirthdayDown:=formatdatetime('yyyy-mm-dd',dtBirthday);</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>strBirthdayUp:=formatdatetime('yyyy-mm-dd',dtBirthday+strtoint(retirePara._uptime));</span></p>
<p><span>&nbsp;_qry.Close;</span></p>
<p><span>&nbsp;_qry.SQL.Clear;</span></p>
<p>&nbsp;</p>
<p><span>&nbsp;strSQL:='select employeeid,employeename,dptname,birthday,sex,EMPLOYEETYPE';</span></p>
<p><span>&nbsp;strSQL:=strSQL +'&nbsp;from HR1_EMPLOYEE left join hr1_workdept on hr1_employee.workdeptid=hr1_workdept.dptid ';</span></p>
<p><span>&nbsp;strSQL:=strSQL +'&nbsp;where sex=:sex';</span></p>
<p><span>&nbsp;&nbsp;strSQL:=strSQL +' and EMPLOYEETYPE=:EMPLOYEETYPE and BIRTHDAY between '+''''+strBirthdayDown+''''+' and '+''''+strBirthdayUp+'''';&nbsp;strSQL:=strSQL+'order by dptid asc';</span></p>
<p><span>&nbsp;_qry.SQL.Text :=strSQL;</span></p>
<p><span>&nbsp;_qry.ParamByName('EMPLOYEETYPE').AsString :=retirePara._emp_type;</span></p>
<p><span>&nbsp;_qry.ParamByName('SEX').AsString :=retirePara._sex;</span></p>
<p><span>&nbsp;_qry.Open;</span></p>
<p>&nbsp;</p>
<p><span>&nbsp;<strong>if</strong> _qry.RecordCount&gt;0 <strong>then</strong></span></p>
<p><span>&nbsp;<strong>begin</strong></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span><strong>while</strong> <strong>not</strong> _qry.Eof do<strong></strong></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span><strong>begin</strong></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>employeeRetired:=TEmployeeRetired.Create;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>employeeRetired._ID:=_qry.FieldByName('employeeid').AsString;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span><em><span>&nbsp;&nbsp;</span><span>&nbsp;//</span></em></span><em><span>以下省略若干代码</span></em><em></em></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>employeesRetired.Add(employeeRetired);</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span><strong>end</strong>;</span></p>
<p><span>&nbsp;<strong>end</strong>;</span></p>
<p><span>&nbsp;retireAwoke:=employeesRetired;</span></p>
<p><strong><span>end</span></strong><span>;</span></p>
</div>
<p><span>很高兴，测试通过了！不过实话实说，也并非一次就能做成功的，其中</span><span>SQL</span><span>语句写错了，就查了半天，</span><span>DUnit</span><span>报错说</span><span>missing expression</span><span>。我就是一个这样马虎的人，很难把程序一下子写对，有</span><span>DUnit</span><span>做保证，小步前进，以免陷入绝境，在</span><span>Bug</span><span>丛生的密林中寻找，当是非常痛苦的事情了。</span></p>
<h2><span><span>3.5<span> </span></span></span><span>重构</span></h2>
<p><span>对于</span><span>XP</span><span>，我觉得简单设计、</span><span>TDD</span><span>、重构、持续集成、小规模发布是连在一起的实践。简单设计而没有重构，就变成了</span><span>Code and Fix</span><span>。只有重构，而没有单元测试的保证，无异于徒手穿越原始森林，没有安全保证。单元测试，继而持续集成、小规模发布，才能实现工作的软件。再加之结对编程，以提高代码质量；完全客户现场，以捕获最真实的需求和得到最真实的反馈，以最快速最真实的态度响应变化；集体代码所有制，以减少人员流动的风险，和提高复用；</span><span>40</span><span>小时工作日，以避免累死和创建更优质的代码。这是我体会到的</span><span>XP</span><span>实践的好处，随着实践和思考的增多，我觉得自己越来越认同</span><span>XP</span><span>的观点了。</span></p>
<p><span>闲言少叙，继续编程。继续完成其余三种情况，目前而言，就只有四种类型的员工。</span></p>
<p><u><span>&nbsp;|<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></u><u><span>管理人员</span><span>&nbsp;&nbsp;|<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></u><u><span>工人</span></u></p>
<p><span>男</span><span>|<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>|</span></p>
<p><span>女</span><span>|<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>|</span></p>
<p><span>从注释（</span><span>//</span><em><span>满足条件的男管理人员</span></em><span>）开始，下面每一类型都要重复一次代码，当然，可以直接在其中写循环语句，但是看到</span><span>Too Long Method</span><span>恐惧症，大量的查询代码混在这个方法，让我觉得很别扭，于是我觉得将它们重构出来，采用</span><span>Extract Method</span><span>。</span></p>
<p><span>首先新建</span><span>getretireAwokeList</span><span>方法，将从数据库中取出需要被提醒的退休人员的代码</span><span>Extract</span><span>到其中。然后整理局部变量。</span></p>
<p><span>不断的编译，来帮助我检查错误，是不是某些局部变量没有迁移过来，有没有变量重名等。经过一番折腾，编译通过，运行测试。</span></p>
<p><span>进行测试，悲剧发生了，在显示绿条后死机了，我想可能是内存释放有问题了。</span></p>
<p><span>结果果然如此，下面这段程序结束后没有释放</span><span>employeeRetiredList</span><span>。</span></p>
<div>
<p><em><span>{ </span></em><em><span>作者：零雨其蒙</span></em><em></em></p>
<p><em><span>创建时间：</span></em><st1:chsdate w:st="on" year="2007" month="5" day="24" islunardate="False" isrocdate="False"><em><span>2007-5-24</span></em></st1:chsdate><em><span> </span></em></p>
<p><em><span>Blog:blog.csdn.net/sslaowan</span></em></p>
<p><em><span><span>&nbsp;&nbsp;&nbsp; </span>www.blogjava.net/sslaowan</span></em></p>
<p><em><span>}</span></em></p>
<p><strong><span>procedure</span></strong><span> TTestRetire.testRetireAwoke;</span></p>
<p><strong><span>var</span></strong><span> employeeRetiredList:TObjectList;</span></p>
<p><span><span>&nbsp;&nbsp; </span></span><span><span>&nbsp;&nbsp;&nbsp;&nbsp;</span></span><span>&nbsp;i:integer;</span></p>
<p><strong><span>begin</span></strong></p>
<p><span>&nbsp;</span><span><span>&nbsp;&nbsp;&nbsp;&nbsp;</span></span><span>&nbsp;<span>employeeRetiredList:=TObjectList.Create;</span></span></p>
<p><span><span>&nbsp;&nbsp; </span></span><span><span>&nbsp;&nbsp;&nbsp;&nbsp;</span></span><span>_employeeRetired:=TEmployeeRetired(employeeRetiredList.Items[0]);</span></p>
<p><span>&nbsp;</span><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></span><span>CheckEquals('</span><span> </span><span>YG000046',_employeeRetired</span><span>._ID</span><span>);</span></p>
<p><span>CheckEquals('</span><span>李隆基</span><span>',_employeeRetired</span><span>._Name</span><span>);</span></p>
<p><span>employeeRetiredList</span><span>.<span>Free</span>;</span></p>
<p><strong><span>end;</span></strong></p>
</div>
<p><span>释放了</span><span>employeeRetiredList</span><span>之后，其中的所有对象也就跟着被释放了。</span></p>
<h2><span><span>3.6<span> </span></span></span><span>整合<span>UI</span></span></h2>
<p><span>最后一步，设计一个展示被提醒的退休人员信息的</span><span>Form</span><span>，然后放置一个叫做</span><span>sgdRetire</span><span>的</span><span>StringGrid</span><span>，这时就是通过列表循环赋值到</span><span>StringGird</span><span>中就可以了。</span></p>
<div>
<p><em><span>{ </span></em><em><span>作者：零雨其蒙</span></em><em></em></p>
<p><em><span>创建时间：</span></em><st1:chsdate w:st="on" year="2007" month="5" day="24" islunardate="False" isrocdate="False"><em><span>2007-5-24</span></em></st1:chsdate><em><span> </span></em></p>
<p><em><span>Blog:blog.csdn.net/sslaowan</span></em></p>
<p><em><span><span>&nbsp;&nbsp;&nbsp; </span>www.blogjava.net/sslaowan</span></em></p>
<p><em><span>}</span></em></p>
<p>&nbsp;</p>
<p><strong><span>procedure</span></strong><span> TForm1.showEmployeeRetired;</span></p>
<p><strong><span>var</span></strong><span> employeeRetiredList:TObjectList;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>i:integer;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>_employeeRetired:TEmployeeRetired;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span>retire:TRetire;</span></p>
<p><strong><span>begin</span></strong></p>
<p><span><span>&nbsp;&nbsp; </span>sgdRetire.Cells[0,0]:='</span><span>员工编号</span><span>';</span></p>
<p><span><span>&nbsp;&nbsp; </span>sgdRetire.Cells[1,0]:='</span><span>姓名</span><span>';</span></p>
<p><span><span>&nbsp;&nbsp; </span>sgdRetire.Cells[2,0]:='</span><span>部门</span><span>';</span></p>
<p><span><span>&nbsp;&nbsp; </span>sgdRetire.Cells[3,0]:='</span><span>生日</span><span>';</span></p>
<p><span><span>&nbsp;&nbsp; </span>sgdRetire.Cells[4,0]:='</span><span>性别</span><span>';</span></p>
<p><span><span>&nbsp;&nbsp; </span>sgdRetire.Cells[5,0]:='</span><span>工种</span><span>';</span></p>
<p><span><span>&nbsp;&nbsp; </span>sgdRetire.Cells[6,0]:='</span><span>距离退休天数</span><span>';</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp; </span>retire:=TRetire.Create;</span></p>
<p><span><span>&nbsp;&nbsp; </span>employeeRetiredList:=retire.retireAwoke(qryRetire);</span></p>
<p><span><span>&nbsp;&nbsp; </span>sgdRetire.RowCount:= employeeRetiredList.Count+1;</span></p>
<p><span><span>&nbsp;&nbsp; </span><strong>for</strong> i:=0 <strong>to</strong>&nbsp;employeeRetiredList.Count-1 <strong>do</strong></span></p>
<p><span><span>&nbsp;&nbsp; </span><strong>begin</strong></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span>_employeeRetired:=TEmployeeRetired(employeeRetiredList.Items[i]);</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span>sgdRetire.Cells[0,i+1]:=_employeeRetired._ID;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span>sgdRetire.Cells[1,i+1]:=_employeeRetired._Name;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span>sgdRetire.Cells[2,i+1]:=_employeeRetired._Dept;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span>sgdRetire.Cells[3,i+1]:=_employeeRetired._Birthday;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span>sgdRetire.Cells[4,i+1]:=_employeeRetired._Sex;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span>sgdRetire.Cells[5,i+1]:=_employeeRetired._WorkType;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span>sgdRetire.Cells[6,i+1]:=_employeeRetired._DaysLeft;</span></p>
<p><span><span>&nbsp;&nbsp; </span><strong>end</strong>;</span></p>
<p><span><span>&nbsp;&nbsp; </span>employeeRetiredList.Free;</span></p>
<p><span><span>&nbsp;&nbsp; </span>retire.Free;</span></p>
<p><strong><span>end</span></strong><span>;</span></p>
</div>
<p><span>但是一定要注意：对象的释放问题，对象生命周期的开始到完结，一定要好好想清楚。不知道那些痴迷与</span><span>C/C++</span><span>的开发者，是如何处理内存释放问题的，我的</span><span>Delphi</span><span>面向对象编程经验还不算多，对我而言，仔细分析每个对象内存是否被释放了，真的是一件非常痛苦的事情。所以还是比较喜欢</span><span>Java</span><span>那样带垃圾回收器的语言。但是通过创建和释放对象，来理解对象的生命周期意义，对于理解对象还是很有帮助的。</span></p>
<h1><span><span>4<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>真的要选择</span><span>TDD</span><span>吗？</span></h1>
<p><span>仔细的设计测试用例，不仅是在思考对象具有哪些责任，同时也是在思考对象如何使用。先总结一下使用</span><span>TDD</span><span>的几点好处：</span></p>
<p><span><span>1.<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>可以不断地测试，以保证代码是正确的。而且在正在开发的这一段时间内，测试是可以不断进行的，期望的结果不会有什么大的变动。（时间长了就不好说了，比如本文给出的例子。）</span></p>
<p><span><span>2.<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>由于有了可以重复进行的测试保障，因此可以大胆的进行重构了。</span></p>
<p><span><span>3.<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>在编写产品代码之前考虑什么情况是正确的，而且可以编写代码边增加测试数据，这样可以有利于全面的测试。据说配合测试装置，还可以由业务人员填入测试数据，这样样本就更大了。</span></p>
<p><span><span>4.<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>因此结果是骗不了人的，当红条亮起时，你就知道是刚刚编写的那段代码错了。</span></p>
<p><span><span>5.<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>在编写测试用例时，就是在思考这个对象干什么的时候，这有利于养成针对接口编程的好习惯，同时这样也就自然的为对象分配了职责。</span></p>
<p><span><span>6.<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>在编写测试用例时，还需要考虑这个对象是如何使用的，这无疑就是在编写对象的使用说明书了。</span></p>
<p><span><span>7.<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>由于以上两点，可以使我们为了使对象或对象的方法便于测试，而降低了对象间的耦合，减少了依赖。</span></p>
<p><span><span>8.<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>进行测试驱动开发，还会使得我们倾向于领域驱动开发，而不是</span><span>UI</span><span>驱动或数据库驱动，同时这也有利于将各层解耦。</span></p>
<p><span>另外有以下几点值得思考：</span></p>
<p><span><span>1.<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>测试驱动开发提高总体效率的前提是什么？毫无疑问，从长远来看，测试驱动开发提高了代码质量，而软件的成本往往从维护阶段开始（譬如我们现在正在维护的这个项目，让人欲死欲活的）。但是，我在进行</span><span>TDD</span><span>实践时，确实比直接开发花费了更多时间，包括思考测试用例应该怎样写，比如测试持久层就想了半个多小时。</span></p>
<p><span><span>2.<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>另外，真的要关掉&#8220;异常时中断&#8221;功能吗？有几次总是报</span><span>ORA***:missing expression</span><span>错误，我真的想设个断点看看到底</span><span>SQL</span><span>语句成什么样了。虽然错误的发生就在那两三行中，或许某一个不超过</span><span>20</span><span>行的函数中，但是我还是花费了很多时间去反复测试，仔细看代码，观察到底在哪错了，因为</span><span>DUnit</span><span>不会告诉你是哪行错了。（或许有告诉，我不知道在哪而已）</span></p>
<p><span><span>3.<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>不知道为什么，我是将产品代码所在的文件夹放在了</span><span>search path</span><span>中了，可是总会遇到产品代码更新了，测试代码那边读到的还不是最新结果。刚开始，我写到会出现那个代码已更改的提醒，之后代码就同步了，可是后来重启了一次</span><span>Delphi</span><span>就不行了。</span></p>
<p><span><span>4.<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>就是内存释放问题，在测试代码中和产品代码中都要考虑内存释放问题，很烦。</span></p>
<p><span>虽然在进行</span><span>TDD</span><span>实践过程中，碰到了不少挫折，不过总体而言，我觉得驱动测试开发让我在编写测试用例时思考对象的工作方式，是一种渐进的思考过程。其实，我一直的编程习惯是，面对一个问题先花费一两天时间思考，把各种关系都搞清楚了，然后在两个小时内一气呵成，由于思考的很清楚，因此错误也挺少的。</span><span>Planned Design</span><span>和进化式设计两种方式都让我感到获益，</span><span>XP</span><span>之所以叫做极限编程，其中一个极限的部分可能就是其简单设计的极限，不需要任何的架构设计，就根据用户故事开始编程。不过我觉得我需要更多的实践</span><span>TDD</span><span>，那些编写测试用例遇到的困难，我想或许每个新手都会遇到，唯有多多实践才能真正的提高。</span></p>
<h1><span><span>5<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>大项目的思考</span></h1>
<p><span>在大项目和大的团队中推行</span><span>TDD</span><span>是有困难的。</span></p>
<p><span><span>1.<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>首先，</span><span>TDD</span><span>需要编写产品代码以外的测试代码，很多程序员为了快速完成任务（有些人的时间只够编写产品代码），不会愿意写测试代码的。虽然它从长远考虑会有很多好处，但是现在的程序员有多少会想很远呢（这也跟责任心有关）？可能等到维护时，我都走人了。</span></p>
<p><span><span>2.<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>其次，</span><span>TDD</span><span>需要开发人员有很强的设计能力，在这里我讨论</span><span>OO</span><span>设计，不过我发现，在中国，程序员对</span><span>OO</span><span>都知之甚少。更甭说进行优秀的</span><span>OO</span><span>设计了。况且，</span><span>Delphi</span><span>不支持垃圾回收，习惯于&#8220;拖拉机&#8221;方式的程序员估计要造成很多混乱了。</span></p>
<p><span><span>3.<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>最后，比如我们这样的大型项目，数百张数据表，上千个窗体，如何进行</span><span>TDD</span><span>，如果我自己没做过，真是很难说服老板推荐这么干。虽然我坚信，这是可行的。</span></p>
<p><span>或许在您的项目中，已经成功地应用了</span><span>TDD</span><span>，那么希望您能够分享您的经验。如果您经历的大型项目正在使用</span><span>TDD</span><span>，那么我们所有的读者都将非常感兴趣。</span></p>
<p>&nbsp;</p>
<p><span>希望本文有任何不足之处，都请与我联系，在我的</span><span>Blog</span><span>上留言或给我发邮件：</span><span>sslaowan@gmail.com</span></p>
<p>&nbsp;</p>
<img src ="http://www.blogjava.net/sslaowan/aggbug/119830.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sslaowan/" target="_blank">零雨其蒙</a> 2007-05-24 22:31 <a href="http://www.blogjava.net/sslaowan/articles/119830.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[翻译]Driven to … Discovering Your Design Values</title><link>http://www.blogjava.net/sslaowan/articles/118812.html</link><dc:creator>零雨其蒙</dc:creator><author>零雨其蒙</author><pubDate>Mon, 21 May 2007 02:17:00 GMT</pubDate><guid>http://www.blogjava.net/sslaowan/articles/118812.html</guid><wfw:comment>http://www.blogjava.net/sslaowan/comments/118812.html</wfw:comment><comments>http://www.blogjava.net/sslaowan/articles/118812.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sslaowan/comments/commentRss/118812.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sslaowan/services/trackbacks/118812.html</trackback:ping><description><![CDATA[&nbsp;
<p align=left><strong><span>Driven to &#8230; Discovering</span></strong></p>
<p><strong><span>Your Design Values</span></strong></p>
<p><strong><span>驱动&#8230;&#8230;发现你的设计价值</span></strong><strong></strong></p>
<p align=left><strong><span>Rebecca J. Wirfs-Brock</span></strong></p>
<p align=left><em><span>There will be variations of everything forever. &#8230; Ideas don&#8217;t disappear. They change form,</span></em></p>
<p><em><span>they merge with other ideas. </span></em><span>—Bob Frankston, coinventor of VisiCalc</span></p>
<p><em><span>万物永远都是变化的&#8230;&#8230;思想没有消失。它们改变了形式，它们并入了其他思想。</span></em><span>——</span><span>Bob Frankston</span><span>，</span><span>VisiCalc</span><span>发明者</span></p>
<p align=left><span>T</span><span>oday we have design or development approaches that are, for example, responsibility&nbsp;driven (RDD), test driven (TDD),behavior driven (BDD), domain driven(DDD), and model driven (MDD). Not all thought leaders in software development have been &#8220;driven&#8221;—Bertrand Meyer, for example, invented Design by Contract. But whether &#8220;driven&#8221; or not, these approaches all emphasize a core set of values and principles around which practices, techniques, and tools have emerged.</span></p>
<p align=left><span>&nbsp;&nbsp;</span><span>今天我们拥有的设计或开发方法是，举个例子，责任驱动（</span><span>RDD</span><span>），测试驱动（</span><span>TDD</span><span>），行为驱动（</span><span>BDD</span><span>），领域驱动（</span><span>DDD</span><span>）和模型驱动（</span><span>MDD</span><span>）。并不是软件开发中的所有领导者都是&#8220;驱动&#8221;的——比如</span><span>Bertrand Meyer</span><span>发明的契约式设计。但是无论是不是&#8220;驱动&#8221;，这些方法都强调了一组围绕已经出现的实践、技术和工具的核心价值和准则。</span><span><span>&nbsp;&nbsp; </span></span></p>
<p align=left><span><span>&nbsp;&nbsp; </span>A thoughtful designer should be able to pick and choose among practices without losing their essence. But not all practices are congruent. After stewing in this alphabet soup for years, I&#8217;m keen on exposing the common and complementary threads that are interwoven among various design practices.</span></p>
<p align=left><span><span>&nbsp;&nbsp; </span></span><span>一个有思想的设计者应该能够在这些实践中挑选而不失去它们的本质。但是不是所有的实践都是适合的。在这个字母汤里煮了多年之后，我热衷于揭示交织在各种各样的设计实践中的共通之处和补充内部。</span></p>
<p align=left><strong><span>Responsibility-driven design</span></strong></p>
<p align=left><strong><span>责任驱动设计</span></strong><strong></strong></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span>So how can you integrate various practices without watering them down or muddling your thinking with too many considerations? It&#8217;s certainly easy if you have one belief system with one small set of coherent values and practices that guide your work. In 1989, Brian Wilkerson and I authored the paper &#8220;Object-Oriented Design:A Responsibility-Driven Approach.&#8221;1 For better or worse, we started the trend of tagging design approaches as &#8220;driven.&#8221; To make our point, we oversimplistically divided object design </span><span>approaches into two camps: those that focus first on structure and those that focus first on action (or responsibilities or behaviors). We argued that designers who first focus only on an object&#8217;s structure fail to maximize encapsulation.</span></p>
<p align=left><span>Thinking too early about structure makes it too easy for implementation details to bleed into class interfaces.</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp; </span></span><span>所以你怎样将各种不同的实践整合起来而不会由于有太多需要考虑的事项将它们全都随水一起倒掉或搞乱你的思想？如果你有一个信任系统和一小套一致的价值和实践指导你工作，那么这确实很容易。在</span><span>1989</span><span>年，我和</span><span>Brian Wilkerson</span><span>写了名为&#8220;面向对象设计：一种责任驱动的方法&#8221;的论文。为了更好或更糟，我们开始倾向于给设计方法贴上&#8220;驱动&#8221;的标签。为了声明我们的观点，我们过分单纯的将对象设计方法分为两个阵营：一派首先关注结构，而另一派首先关注动作（或责任或行为）。我们认为首先只是关注一个对象的结构的设计者未能实现最佳的封装。过早的考虑结构会导致过早的将实现细节侵染到类接口。</span></p>
<p align=left><span>We contrasted two approaches for designing a </span><span>RasterImage </span><span>class that represented a rectangular grid of pixels. (We wrote this paper when raster technology was new, and at the time we both worked at Tektronix, a leading provider of&nbsp;graphics workstations.) With a data-first approach,we started defining our </span><span>RasterImage </span><span>class by declaring the image data structure and then adding methods to retrieve and set the image and query its dimensions. Voila!—a class where form and function were inextricably intertwined.</span></p>
<p align=left><span>我们对比两种设计表现像素的一个矩形栅格的</span><span>RasterImage</span><span>类的方法。（我们写这篇论文时，光栅技术还是新的，那时我们一起在</span><span>Tektronix</span><span>工作，</span><span>Tektronix</span><span>是图形工作站的主要提供商。）用数据先行的方法，我们开始定义我们的</span><span>RasterImage</span><span>类，我们声明镜像数据结构，然后添加方法去重新得到和设置镜像，查询它的尺寸。瞧！——一个外形和功能避免不了的纠缠在一起的类。</span></p>
<p align=left><span>The pixel grid data structure wasn&#8217;t&nbsp;considered a private implementation detail.Next, we demonstrated how a designer could think differently about the problem by asking,&#8220;What actions could this object be responsible for?&#8221; and &#8220;What information should it share with others?&#8221; This led us to first define operations for our </span><span>RasterImage </span><span>class to scale and rotate the image and access pixel values. The internal image &nbsp;representation, which we didn&#8217;t specify until after we&#8217;d defined the interface,was considered a private detail.</span></p>
<p align=left><span>这个像素栅格数据结构不是被考虑成一个私有的实现细节。下一步，我们证明一个设计者能够以不同的方式来思考问题，通过询问&#8220;这个对象对于什么动作负责任？&#8221;和&#8220;它和别人共享什么信息&#8221;。这引导我们首先定义我们的</span><span>RasterImage</span><span>类的操作来测量和旋转镜像和访问像素值。我们在定义接口之前没有指定的内部镜像表示被考虑成一个私有细节。</span></p>
<p><span>By consciously assigning most objects actionoriented responsibilities, you can design even seemingly &nbsp;data-centric objects to perform some actions as well as encapsulate structural details.</span><span> </span>
<table cellSpacing=0 cellPadding=0 width="100%">
    <tbody>
        <tr>
            <td>
            <div>
            <p align=center><strong><span>DESIGN</span></strong></p>
            </div>
            </td>
        </tr>
    </tbody>
</table>
<table cellSpacing=0 cellPadding=0 width="100%">
    <tbody>
        <tr>
            <td>
            <div>
            <p><strong><span>1 0 </span></strong><strong><span>IEEE SOFTWARE <a href="http://www.computer.org/software"><em><span>www.computer.org/software</span></em></a></span></strong><em></em></p>
            </div>
            </td>
        </tr>
    </tbody>
</table>
<span>Hiding that structure makes those de&shy;tails easier to change. To us it seemed that the order in which a designer con&shy;siders things profoundly affects the re&shy;sulting design—even for a class as straightforward as <span>RasterImage. </span>To quote Samuel Alexander, the philoso&shy;<span>pher, &#8220;An object is not first imagined or</span> thought about and then expected &#8230; but <span>in being actively expected it is imagined</span> as future and in being willed it is thought.&#8221;</span></p>
<p><span>通过自觉地分配给大多数对象面向动作的责任，你甚至能够设计看起来以数据为中心的对象来执行一些动作和封装结构的细节。隐藏结构使那些细节容易改变。对于我们而言，看起来一个设计者考虑事情的顺序深深的影响作为结果的设计——甚至对于一个像</span><span>RasterImage</span><span>一样简单易懂的类。引用哲学家</span><span>Samuel Alexander</span><span>的话，&#8220;一个对象不是先被想像或思考然后被期望&#8230;&#8230;但是由于积极的被期望，它被想像成未来，由于有某种意志，它是思想。&#8221;</span></p>
<p><span>&nbsp;Since those early days I&#8217;ve added the</span><span> notion of role stereotypes,<sup><span> 2</span></sup> acknowledg&shy;ing that not all objects are active. Infor&shy;mation holders—objects with responsi&shy;<span>bility for maintaining data—have a place</span> in a design, too. But encapsulating their private details is important.</span></p>
<p><span>&nbsp;&nbsp;</span><span>自从那些早些的日子我添加了角色构造型的概念，知道不是所有的对象都是有效的。信息持有者——具有持有数据责任的对象——也在一个设计中有一席之地。但是封装它们的私有细节是重要的。</span></p>
<p><strong><span>Test-driven design</span></strong></p>
<p><strong><span>测试驱动设计</span></strong><strong></strong></p>
<p><span>&nbsp;</span><span>&nbsp;RDD evolved in the highly interactive</span><span> world of Smalltalk development, where developers routinely designed a little, coded a little, and tested a little in short <span>cycles. The delightful tension of cycling</span> <span>between imagining what an object might</span> do, building it, and then refining your ideas and cycling through your design <span>again can lead to deep insights. This has</span> led agile-programming thought leaders to promote test-driven development <span>practices. Test-driven design emphasizes</span> deciding on an interface and then writ&shy;ing code to test that interface, before im&shy;plementing code to make the interface pass the test.</span></p>
<p><span>&nbsp;RDD</span><span>在高度互动的</span><span>Smalltalk</span><span>开发世界进化，在那儿开发者例行公事的设计一小块，编一小段代码，然后再很短的周期里做少许测试。在想象一个对象做什么，然后构建它，然后精化你的想法之间循环的令人愉快的压力而且循环通过你再一次设计能够使你有更深的理解。这引导敏捷开发思想的领导者提倡测试驱动开发实践。测试驱动设计强调动对一个接口作出决定，然后编写代码去测试那个接口，在实现代码之前使接口通过测试。</span></p>
<p><span>In <em><span>Test-Driven Development by Example, </span></em>Martin Fowler claims that TDD <span>&#8220;gives you this sense of keeping just one</span> ball in the air at once, so you can con&shy;centrate on that ball properly and do a <span>really good job with it.</span>&#8221;<sup><span>3</span></sup><span> With a design</span>&shy;test-code-reflect-refactor rhythm, good code emerges alongside well-designed interfaces.</span></p>
<p><span>在</span><em><span>Test-Driven Development by Example</span></em><span>中，</span><span>Martin Fowler</span><span>声称</span><span>TDD</span><span>&#8220;给你这种保持一次只有一个球在空中的感觉，所以你可以完全集中精力在那个球上和使用它真正地做好一件事。&#8221;带着一种&#8220;设计测试</span><span>-</span><span>编码</span><span>-</span><span>反馈</span><span>-</span><span>重构&#8221;的节奏，好的代码在一旁显示出设计良好的接口。</span></p>
<p><span>Linda Crispin explains that TDD isn&#8217;t</span><span> <span>really about testing</span>.<sup><span>4</span></sup><span> Instead, it&#8217;s a prac</span>&shy;tice that gets you thinking about as many aspects of a feature as you can be&shy;fore you code it. With frameworks such as Fit and Fitnesse, TDD has extended beyond its initial focus on just develop&shy;ers to enable nontechnical people to write concrete examples of inputs and expected results in tabular form. Programmers write test fixtures that use these behavioral specifications to test the code.</span></p>
<p><span>Lina Crispin</span><span>说明</span><span>TDD</span><span>其实不是关于测试的。相反，它是一种使你在编码之前尽可能考虑一个特征的许多方面的实践。使用像</span><span>Fit</span><span>和</span><span>Fitness</span><span>这些框架，</span><span>TDD</span><span>扩大了它最初的关注范围，那时它只关注开发者使非技术人员能够在表格中写出具体的输入样板和期望的结果。程序员编写测试装置来测试代码，测试装置使用这些行为的详细规格说明书。</span></p>
<p><span>&nbsp;As TDD practices have grown, new variants of them, along with newer test&shy;<span>ing frameworks, have emerged. Users of</span> jMock use <em><span>mocks </span></em>that mimic unimple&shy;mented behaviors to drive out an appro&shy;priate distribution of responsibilities <span>among collaborators</span> <span>.<sup><span>5 </span></sup>They don&#8217;t think</span> <span>that it&#8217;s just about testing, either. Mock</span>&shy;ing lets you incrementally design and build software, hypothesizing and refin&shy;ing your ideas as you go.</span></p>
<p><span>&nbsp;</span><span>作为一个</span><span>TDD</span><span>实践者已经成长，它们中的新的变体和更新的测试框架已经形成。</span><span>jMock</span><span>的使用者使用</span><span>mocks</span><span>来模仿未实现的行为来完成在协作者中一个适当责任的分配。他们不认为只是测试。</span><span>Mock</span><span>让你增量的设计和构建软件，当你前进时假定和精炼你的想法。</span></p>
<p><strong><span>Behavior-driven design</span></strong></p>
<p><strong><span>行为驱动设计</span></strong><strong></strong></p>
<p><span>&nbsp;BDD is another subtle refinement of TDD. BDD proponents firmly believe that how you talk about what you&#8217;re do&shy;ing influences how you work. The focus is on writing small behavior specifica&shy;tions to drive out the appropriate design. As Dave Astels puts it, &#8220;A major differ&shy;ence is vocabulary. Instead of subclass&shy;ing TestCase [as you would do using an xUnit framework], you subclass Con&shy;text. Instead of writing methods that start with test, you start them with should.&#8221;6 Testing, to BDD proponents, connotes verifying code after it&#8217;s built. Instead, they want to encourage incre&shy;mental design by writing small specifica&shy;tions, then implementing code that works according to spec.</span></p>
<p><span>BDD</span><span>是</span><span>TDD</span><span>另一种的细微的改进。</span><span>BDD</span><span>支持者坚定的相信你如何讨论你正在做的事情会影响你如何工作。焦点是编写小的行为规范来导出合适的设计。当</span><span>Dave Astels</span><span>提出它时，&#8220;一个主要的不同是词汇。代替子集化</span><span>TestCase [</span><span>当你使用一个</span><span>xUnit</span><span>框架时</span><span>]</span><span>，你子集化上下文。代替书写使用测试开始的方法，你使用意愿开始。&#8221;测试，对于</span><span>BDD</span><span>的支持者，意味着在构建之后检验代码。相反，他们想要鼓励增量设计通过书写小的规格说明书，然后实现按照规格说明书工作的代码。</span></p>
<p align=center><strong><span>Does every method<br>warrant a contract?<br>Probably not. Methods<br>that don&#8217;t cause side<br>effects probably don&#8217;t<br>need contracts.</span></strong></p>
<p><strong><span>Design by Contract</span></strong></p>
<p><strong><span>契约式设计</span></strong><strong></strong></p>
<p><span>In contrast, Design by Contract (DbC) has roots in formal specifica&shy;tions. To specify how they expect sys&shy;tem elements to interact, designers write contracts specifying what must be true before a module can begin (precon&shy;ditions), what must be preserved during its execution (invariants), and what it guarantees to be true after it completes <span>(postconditions). You could specify con</span>&shy;tracts for components, services, or even individual methods. However, in prac&shy;tice, most contracts are written at the method level because existing program&shy;ming languages and tools support work at that level. Writing contracts is easier if the languages and tools you use sup&shy;port them and you have good examples to emulate. The Eiffel language inte&shy;grates contract support into the lan&shy;guage and runtime environment; most other object-oriented languages don&#8217;t.</span></p>
<p><span>与之对照的，契约式设计根植于正式的规格说明。为了指出他们期望系统要素如何相互作用，设计者撰写契约详细说明在一个模块开始之前什么是必须正确的（前置条件），在执行时什么是必须保持不变的（不变式），在它完成之后它保证什么是正确的。你应该详细说明组件、服务或者甚至是单独的方法的契约。然而，在实践中，更多的契约是在方法级上书写的，因为既有的编程语言和工具支持工作在那个层面上。如果你使用的语言和工具支持他们而且你有好的例子仿效那么撰写契约是更容易的。</span><span>Eiffel</span><span>语言将契约支持整合进语言和运行时环境；大多数其他的面相对象语言不具备这种支持。</span></p>
<p><span>Before looking at Contract4J, a DbC tool for Java, I thought that spec&shy;ifying contracts for languages without built-in support would be clunky. However, using aspect technology, Contract4J automatically weaves as&shy;pect-specific contract tests, which are specified in method comments, into your running code. Contracts specified this way leave method code unclut&shy;tered with assertion statements and leave a nice documentation trail of how methods should be invoked.</span></p>
<p><span>在看到</span><span>Contract4J</span><span>（</span><span>Java</span><span>的</span><span>DbC</span><span>工具）之前，我认为为一种语言指定契约而没有内置的支持将会非常郁闷。然而，使用方面技术。</span><span>Contract4J</span><span>自动将细节方面的契约测试织入你运行的代码，它在方法注释中被指出，被这种方式指出的契约使用断言声明使方法代码保持整洁，保留一份好的文档跟踪方法是如何被调用的。</span></p>
<p><span>If you choose to, you could apply TDD practices to understand your <span>classes&#8217; behaviors and then add contracts</span> to methods whose behaviors you want verified at runtime. But I suspect these two communities differ considerably in their thinking. Some TDD proponents <span>want to discourage after-the-fact verifica</span>&shy;<span>tion, which to them seems antithetical to</span> designing for quality. But adding con&shy;tracts does tighten up how you use classes, theoretically making it easier to catch errors before they propagate.</span></p>
<p><span>如果你做出选择，你可以应用</span><span>TDD</span><span>实践理解你的类的行为然后将契约加入到你想要在运行时检验其行为的方法中。但是我怀疑这两个社区在他们的思想中相当不同。一些</span><span>TDD</span><span>支持者反对事后检验，这对他们而言看起来与为了质量设计是对立的。但是增加契约加强了你如何使用类，理论上使它更容易在他们传播之前捕捉到错误。</span></p>
<p><span><span>&nbsp;&nbsp; </span>When you change your design, sometimes contracts will naturally change, too. But once your design ideas settle down, you can finalize or add contractual details. But does every method warrant a contract? Probably not. Methods that don&#8217;t cause side ef</span>
<table cellSpacing=0 cellPadding=0 width="100%">
    <tbody>
        <tr>
            <td>
            <div>
            <p align=center><strong><span>DESIGN</span></strong></p>
            </div>
            </td>
        </tr>
    </tbody>
</table>
<table cellSpacing=0 cellPadding=0 width="100%">
    <tbody>
        <tr>
            <td>
            <div>
            <p align=center></p>
            </div>
            </td>
        </tr>
    </tbody>
</table>
<span>fects probably don&#8217;t need contracts. But</span><span> <span>I know I&#8217;d certainly find it easier to use</span> class libraries if contract specifications were part of their documentation even if they weren&#8217;t validated at runtime.</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span></span><span>当你改变你的设计，有时契约也将自然的改变。但是一旦你的设计想法稳定下来，你能够最终定下来或者添加契约的细节。但是每一个方法都保证一份契约？或许不是这样的，不会导致副作用的方法或许不需要契约。但是我知道我的确发现如果契约详细规格书是它们的文档的一部分就更容易使用类库，即使他们不在运行时被验证。</span></p>
<p><strong><span>Domain-driven design</span></strong></p>
<p><span>&nbsp;</span><span>&nbsp;What about incorporating DDD ideas into your design practice? Accord&shy;ing to Eric Evans, DDD isn&#8217;t a technol&shy;ogy or methodology but &#8220;a way of thinking and a set of priorities, aimed at accelerating software projects that have to deal with complicated domains&#8221; (<a href="http://www.domainlanguage.com/ddd/">www.domainlanguage.com/ddd/</a> <span>index.html). A central activity in DDD is</span> searching for the language that experts use to talk about the problem and then <span>literally reflecting that language in classes</span> and services in a domain layer. Eric be&shy;<span>lieves that, &#8220;If developers don&#8217;t realize</span> that changing code changes the model, then their refactoring will weaken the model rather than strengthen it.&#8221; Creat&shy;ing a domain model is intricately tied to expressing it in working code. Domain&shy;driven design is an active, ongoing process of expressing this domain lan&shy;guage in code.</span></p>
<p><span>&nbsp;</span><span>怎样把</span><span>DDD</span><span>想法融入到设计实践中？按照</span><span>Eric Evans</span><span>的说法，</span><span>DDD</span><span>不是一项技术获方法论而是&#8220;一种思考方式和一组优先级，目的在于促进不得不解决复杂领域的软件项目&#8221;。在</span><span>DDD</span><span>中一项核心活动是寻找专家用来讨论问题的语言，然后逐字的将那种语言反映到位于领域层的类和服务中。</span><span>Eric</span><span>相信，&#8220;如果开发者不了解改变代码时改变模型，然后他们的重构将削弱而不是增强模型。&#8221;创建一个领域模型杂乱的在工作着的代码中被依靠表达它。领域驱动设计是一种积极的、正在进行的使用代码表示领域语言的过程。</span></p>
<p><strong><span>Model-driven design</span></strong></p>
<p><strong><span>模型驱动设计</span></strong><strong></strong></p>
<p><span>In contrast, adherents of MDD (some call it <em><span>model-driven engineering </span></em>to avoid the Object Management Group trademarked term) first develop a plat&shy;<span>form-independent model of their system</span> (usually in UML or a domain-specific language) before translation tools trans&shy;form the model into platform-specific code. MDD practitioners strive to clearly represent system concepts and behaviors with the goal of producing an abstract model, not working code (the translation tools do that for them). This view of model building followed by transformation probably causes the great divide between MDD practition&shy;ers and other design schools—even though they share many common de&shy;sign values. After recently listening to and talking with several well-known MDD proponents who were discussing what constitutes well-designed classes, methods, and components, I found my&shy;self nodding in agreement with many of their design guidelines.</span></p>
<p><span>与之对照的，</span><span>MDD</span><span>的拥护者在使用转换工具将模型转换成具体平台代码之前，先开发一个他们系统的平台无关模型（通常使用</span><span>UML</span><span>货一种领域规范语言）。</span><span>MDD</span><span>开创者们带着生产一个抽象模型而非工作的代码（转换工具替他们来做生产代码的事情）的目的，努力清晰的表现系统概念和行为。模型遵循转换构建的观点或许导致了</span><span>MDD</span><span>开开创者与其他设计学派的巨大分隔——即使他们共享许多通用设计价值。在最近听到和与许多不同的众所周知的</span><span>MDD</span><span>支持者（他们正在讨论什么构成了良好设计的类、方法和组件）讨论之后，我发现自己非常赞同他们的设计指导思想中的很多内容。</span></p>
<div>
<table cellSpacing=0 cellPadding=0 vspace="0" hspace="0">
    <tbody>
        <tr>
            <td vAlign=top align=left>
            <p><span>H</span></p>
            </td>
        </tr>
    </tbody>
</table>
</div>
<p><span>ow you design should be based on your principles and values. Al&shy;though a big division exists between those who believe the act of coding is <span>what validates the design and those who</span> <span>don&#8217;t, you can learn many things about</span> good design from each. My mantra has <span>always been, &#8220;Be open to new ideas and</span> techniques that make me a better de&shy;<span>signer.&#8221; I side with Canadian politician</span> <span>Dan Miller, who proclaims, &#8220;You know,</span> we have our differences, everybody does, honest, real differences, but I do <span>believe strongly that we as neighbors are</span> <span>drawn together far more than we&#8217;re dri</span>&shy;ven apart.&#8221;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>如何设计，你应该基于你的原则和价值。尽管有巨大的区别存在于那些确信代码的行为是检查设计的标尺的人们和他们的反对者中，但是你从他们中能够学会许多优秀设计的东西。我的箴言一直是，&#8220;善于接受新观点和技术，使我成为更优秀的设计师&#8221;。我和加拿大政治家站在一起，他宣称，&#8220;你知道，我们有不同，每个人都一样，诚实的，真实的不同，但是我深信作为邻国我们的共通之处远远多于我们的区别。&#8221;</span></p>
<p><strong><span>References</span></strong></p>
<p><span>&nbsp;1.</span><span> R. Wirfs-Brock and B. Wilkerson, &#8220;Object-Oriented Design: A Responsibility-Driven Ap&shy;proach,&#8221; </span><em><span>Proc. 1989 ACM S</span></em><em><span>IGPLAN </span></em><em><span>Conf.</span></em></p>
<p><em><span>Object-Oriented Programming, Systems, Lan‑</span></em></p>
<p><em><span>guages, and Applications </span></em><span>(O</span><span>OPSLA </span><span>89), ACM Press, 1989, pp. 71&#8211;75.</span></p>
<p align=left><span><span>2.<span>&nbsp;&nbsp;&nbsp; </span></span></span><span>R. Wirfs-Brock, &#8220;Characterizing Classes,&#8221; </span><em><span>IEEE Software, </span></em><span>Mar./Apr. 2006, pp. 9&#8211;11.</span></p>
<p align=left><span><span>3.<span>&nbsp;&nbsp;&nbsp; </span></span></span><span>M. Fowler, </span><em><span>Test-Driven Development by Ex&shy;ample, </span></em><span>Addison-Wesley, 2003.</span></p>
<p align=left><span><span>4.<span>&nbsp;&nbsp;&nbsp; </span></span></span><span>L. Crispin, &#8220;Driving Software Quality: How Test-Driven Development Impacts Software Quality,&#8221; </span><em><span>IEEE Software, </span></em><span>Nov./Dec. 2006, pp. 70&#8211;71.</span></p>
<p align=left><em><span><span>5.<span> </span></span></span></em><span>S. Freeman et al., &#8220;Mock Roles Not Objects,&#8221; </span><em><span>Companion to 19th Ann. ACM S</span></em><em><span>IGPLAN</span></em></p>
<p align=center><em><span>Conf. Object-Oriented Programming, Sys‑</span></em></p>
<p><em><span>tems, Languages, and Applications </span></em><span>(O</span><span>OPSLA </span><span>04), ACM Press, 2004, pp. 236&#8211;246; <a href="http://www.jmock.org/oopsla2004.pdf.">www. jmock. org/oopsla2004.pdf.</a></span></p>
<p><span><span>6.<span>&nbsp;&nbsp;&nbsp; </span></span></span><span>D. Astels, &#8220;A New Look at Test-Driven Devel&shy;</span><span>opment,&#8221; <a href="http://blog.daveastels.com/files/BDD">http://blog.daveastels.com/files/BDD</a> </span><span>_Intro.pdf.</span></p>
<p><strong><span>Rebecca</span></strong><strong><span> J. Wirfs-Brock </span></strong><span>is president of Wirfs-Brock </span><span>Associates and an adjunct professor at Oregon Health &amp; Science University. Contact her at <a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#114;&#101;&#98;&#101;&#99;&#99;&#97;&#64;&#119;&#105;&#114;&#102;&#115;&#45;&#98;&#114;&#111;&#99;&#107;&#46;&#99;&#111;&#109;">rebecca@wirfs-brock.com</a>.</span></p>
<p>&nbsp;</p>
<img src ="http://www.blogjava.net/sslaowan/aggbug/118812.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sslaowan/" target="_blank">零雨其蒙</a> 2007-05-21 10:17 <a href="http://www.blogjava.net/sslaowan/articles/118812.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>