﻿<?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>IT博客-Martin Ding's Garden Plot-随笔分类-Design Patterns</title><link>http://www.cnitblog.com/martin/category/2434.html</link><description /><language>zh-cn</language><lastBuildDate>Tue, 27 Sep 2011 22:40:04 GMT</lastBuildDate><pubDate>Tue, 27 Sep 2011 22:40:04 GMT</pubDate><ttl>60</ttl><item><title>[转]测试驱动开发全功略</title><link>http://www.cnitblog.com/martin/archive/2006/04/27/9813.html</link><dc:creator>Martin</dc:creator><author>Martin</author><pubDate>Thu, 27 Apr 2006 11:51:00 GMT</pubDate><guid>http://www.cnitblog.com/martin/archive/2006/04/27/9813.html</guid><wfw:comment>http://www.cnitblog.com/martin/comments/9813.html</wfw:comment><comments>http://www.cnitblog.com/martin/archive/2006/04/27/9813.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/martin/comments/commentRss/9813.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/martin/services/trackbacks/9813.html</trackback:ping><description><![CDATA[
		<p>From: Brian Sun @ 爬树的泡泡[http://www.blogjava.net/briansun]</p>
		<p>
				<font color="#ff0000" size="5">{关键字}</font>
		</p>
		<p>测试驱动开发/Test Driven Development/TDD<br />测试用例/TestCase/TC<br />设计/Design<br />重构/Refactoring</p>
		<p>
				<font color="#ff0000" size="5">{TDD的目标}</font>
		</p>
		<blockquote>
				<p>
						<font style="font-weight: bold; font-size: large; background-color: rgb(220, 220, 220);">Clean Code That Works</font>
				</p>
		</blockquote>
		<p>这句话的含义是，事实上我们只做两件事情：让代码奏效（Work）和让代码洁净（Clean），前者是把事情做对，后者是把事情做好。想想看，其实
我们平时所做的所有工作，除去无用的工作和错误的工作以外，真正正确的工作，并且是真正有意义的工作，其实也就只有两大类：增加功能和提升设计，而TDD
正是在这个原则上产生的。如果您的工作并非我们想象的这样，（这意味着您还存在第三类正确有意义的工作，或者您所要做的根本和我们在说的是两回事），那么
这告诉我们您并不需要TDD，或者不适用TDD。而如果我们偶然猜对（这对于我来说是偶然，而对于Kent Beck和Martin
Fowler这样的大师来说则是辛勤工作的成果），那么恭喜您，TDD有可能成为您显著提升工作效率的一件法宝。请不要将信将疑，若即若离，因为任何一项
新的技术——只要是从根本上改变人的行为方式的技术——就必然使得相信它的人越来越相信，不信的人越来越不信。这就好比学游泳，唯一能学会游泳的途径就是
亲自下去游，除此之外别无他法。这也好比成功学，即使把卡耐基或希尔博士的书倒背如流也不能拥有积极的心态，可当你以积极的心态去成就了一番事业之后，你
就再也离不开它了。相信我，TDD也是这样！想试用TDD的人们，请遵循下面的步骤：</p>
		<blockquote>
				<font style="font-weight: bold; font-size: large; background-color: rgb(220, 220, 220);">
						<table bgcolor="#dcdcdc" border="0" cellpadding="1" cellspacing="1">
								<tbody>
										<tr>
												<td align="center">编写TestCase</td>
												<td>--&gt;</td>
												<td align="center">实现TestCase</td>
												<td>--&gt;</td>
												<td align="center">重构</td>
										</tr>
										<tr>
												<td align="center">（确定范围和目标）</td>
												<td> </td>
												<td align="center">（增加功能）</td>
												<td> </td>
												<td align="center">（提升设计）</td>
										</tr>
								</tbody>
						</table>
				</font>
		</blockquote>
		<p>[友情提示：敏捷建模中的一个相当重要的实践被称为：Prove it With Code，这种想法和TDD不谋而合。]</p>
		<p>
				<font color="#ff0000" size="5">{TDD的优点}</font>
		</p>
		<ol>
				<p>
						<b>『充满吸引力的优点』</b>
				</p>
				<li>完工时完工。表明我可以很清楚的看到自己的这段工作已经结束了，而传统的方式很难知道什么时候编码工作结束了。 
</li>
				<li>全面正确的认识代码和利用代码，而传统的方式没有这个机会。 
</li>
				<li>为利用你成果的人提供Sample，无论它是要利用你的源代码，还是直接重用你提供的组件。 
</li>
				<li>开发小组间降低了交流成本，提高了相互信赖程度。 
</li>
				<li>避免了过渡设计。 
</li>
				<li>系统可以与详尽的测试集一起发布，从而对程序的将来版本的修改和扩展提供方便。 
</li>
				<li>TDD给了我们自信，让我们今天的问题今天解决，明天的问题明天解决，今天不能解决明天的问题，因为明天的问题还没有出现(没有TestCase)，除非有TestCase否则我决不写任何代码；明天也不必担心今天的问题，只要我亮了绿灯。 

<p><b>『不显而易见的优点』</b></p></li>
				<li>逃避了设计角色。对于一个敏捷的开发小组，每个人都在做设计。 
</li>
				<li>大部分时间代码处在高质量状态，100％的时间里成果是可见的。 
</li>
				<li>由于可以保证编写测试和编写代码的是相同的程序员，降低了理解代码所花费的成本。 
</li>
				<li>为减少文档和代码之间存在的细微的差别和由这种差别所引入的Bug作出杰出贡献。 
</li>
				<li>在预先设计和紧急设计之间建立一种平衡点，为你区分哪些设计该事先做、哪些设计该迭代时做提供了一个可靠的判断依据。 
<p><b>『有争议的优点』</b></p></li>
				<li>事实上提高了开发效率。每一个正在使用TDD并相信TDD的人都会相信这一点，但观望者则不同，不相信TDD的人甚至坚决反对这一点，这很正常，世界总是这样。 
</li>
				<li>发现比传统测试方式更多的Bug。 
</li>
				<li>使IDE的调试功能失去意义，或者应该说，避免了令人头痛的调试和节约了调试的时间。 
</li>
				<li>总是处在要么编程要么重构的状态下，不会使人抓狂。（两顶帽子） 
</li>
				<li>单元测试非常有趣。</li>
		</ol>
		<font color="#ff0000" size="5">{TDD的步骤}</font>
		<blockquote>
				<font style="font-weight: bold; font-size: large; background-color: rgb(220, 220, 220);">
						<table bgcolor="#dcdcdc" border="0" cellpadding="1" cellspacing="1">
								<tbody>
										<tr>
												<td align="center">编写TestCase</td>
												<td>--&gt;</td>
												<td align="center">实现TestCase</td>
												<td>--&gt;</td>
												<td align="center">重构</td>
										</tr>
										<tr>
												<td align="center">（不可运行）</td>
												<td> </td>
												<td align="center">（可运行）</td>
												<td> </td>
												<td align="center">（重构）</td>
										</tr>
								</tbody>
						</table>
				</font>
		</blockquote>
		<table border="0" cellpadding="2" cellspacing="2">
				<tbody>
						<tr>
								<td>步骤</td>
								<td>制品</td>
						</tr>
						<tr>
								<td>（1）快速新增一个测试用例</td>
								<td>新的TestCase</td>
						</tr>
						<tr>
								<td>（2）编译所有代码，刚刚写的那个测试很可能编译不通过 </td>
								<td>原始的TODO List</td>
						</tr>
						<tr>
								<td>（3）做尽可能少的改动，让编译通过</td>
								<td>Interface</td>
						</tr>
						<tr>
								<td>（4）运行所有的测试，发现最新的测试不能编译通过</td>
								<td>－(Red Bar)</td>
						</tr>
						<tr>
								<td>（5）做尽可能少的改动，让测试通过</td>
								<td>Implementation</td>
						</tr>
						<tr>
								<td>（6）运行所有的测试，保证每个都能通过</td>
								<td>－(Green Bar)</td>
						</tr>
						<tr>
								<td>（7）重构代码，以消除重复设计</td>
								<td>Clean Code That Works</td>
						</tr>
				</tbody>
		</table>
		<p>
				<font color="#ff0000" size="5">{FAQ}</font>
		</p>
		<p>
				<b>[什么时候重构？]</b>
				<br />如果您在软件公司工作，就意味着您成天都会和想通过重构改善代码质量的想法打交道，不仅您如此，您的大部
分同事也都如此。可是，究竟什么时候该重构，什么情况下应该重构呢？我相信您和您的同事可能有很多不同的看法，最常见的答案是“该重构时重构”，“写不下
去的时候重构”，和“下一次迭代开始之前重构”，或者干脆就是“最近没时间，就不重构了，下次有时间的时候重构吧”。正如您已经预见到我想说的——这些想
法都是对重构的误解。重构不是一种构建软件的工具，不是一种设计软件的模式，也不是一个软件开发过程中的环节，正确理解重构的人应该把重构看成一种书写代
码的方式，或习惯，重构时时刻刻有可能发生。在TDD中，除去编写测试用例和实现测试用例之外的所有工作都是重构，所以，没有重构任何设计都不能实现。至
于什么时候重构嘛，还要分开看，有三句话是我的经验：实现测试用例时重构代码，完成某个特性时重构设计，产品的重构完成后还要记得重构一下测试用例哦。</p>
		<p>
				<b>[什么时候设计？]</b>
				<br />这个问题比前面一个要难回答的多，实话实说，本人在依照TDD开发软件的时候也常常被这个问题困扰，总是
觉得有些问题应该在写测试用例之前定下来，而有些问题应该在新增一个一个测试用例的过程中自然出现，水到渠成。所以，我的建议是，设计的时机应该由开发者
自己把握，不要受到TDD方式的限制，但是，不需要事先确定的事一定不能事先确定，免得捆住了自己的手脚。</p>
		<p>
				<b>[什么时候增加新的TestCase？]</b>
				<br />没事做的时候。通常我们认为，如果你要增加一个新的功能，那么先写一个不能通过的
TestCase；如果你发现了一个bug，那么先写一个不能通过的TestCase；如果你现在什么都没有，从0开始，请先写一个不能通过的
TestCase。所有的工作都是从一个TestCase开始。此外，还要注意的是，一些大师要求我们每次只允许有一个TestCase亮红灯，在这个
TestCase没有Green之前不可以写别的TestCase，这种要求可以适当考虑，但即使有多个TestCase亮红灯也不要紧，并未违反TDD
的主要精神。</p>
		<p>
				<b>[TestCase该怎么写？]</b>
				<br />测试用例的编写实际上就是两个过程：使用尚不存在的代码和定义这些代码的执行结果。所以一个
TestCase也就应该包括两个部分——场景和断言。第一次写TestCase的人会有很大的不适应的感觉，因为你之前所写的所有东西都是在解决问题，
现在要你提出问题确实不大习惯，不过不用担心，你正在做正确的事情，而这个世界上最难的事情也不在于如何解决问题，而在于ask the right
question！</p>
		<p>
				<b>[TDD能帮助我消除Bug吗？]</b>
				<br />答：不能！千万不要把“测试”和“除虫”混为一谈！“除虫”是指程序员通过自己的努力来减
少bug的数量（消除bug这样的字眼我们还是不要讲为好^_^），而“测试”是指程序员书写产品以外的一段代码来确保产品能有效工作。虽然TDD所编写
的测试用例在一定程度上为寻找bug提供了依据，但事实上，按照TDD的方式进行的软件开发是不可能通过TDD再找到bug的（想想我们前面说的“完工时
完工”），你想啊，当我们的代码完成的时候，所有的测试用例都亮了绿灯，这时隐藏在代码中的bug一个都不会露出马脚来。</p>
		<p>但是，如果要问“测试”和“除虫”之间有什么联系，我相信还是有很多话可以讲的，比如TDD事实上减少了bug的数量，把查找bug战役的关注点从
全线战场提升到代码战场以上。还有，bug的最可怕之处不在于隐藏之深，而在于满天遍野。如果你发现了一个用户很不容易才能发现的bug，那么不一定对工
作做出了什么杰出贡献，但是如果你发现一段代码中，bug的密度或离散程度过高，那么恭喜你，你应该抛弃并重写这段代码了。TDD避免了这种情况，所以将
寻找bug的工作降低到了一个新的低度。</p>
		<p>
				<b>[我该为一个Feature编写TestCase还是为一个类编写TestCase？]</b>
				<br />初学者常问的问题。虽然我们从TDD
的说明书上看到应该为一个特性编写相应的TestCase，但为什么著名的TDD大师所写的TestCase都是和类/方法一一对应的呢？为了解释这个问
题，我和我的同事们都做了很多试验，最后我们得到了一个结论，虽然我不知道是否正确，但是如果您没有答案，可以姑且相信我们。</p>
		<p>我们的研究结果表明，通常在一个特性的开发开始时，我们针对特性编写测试用例，如果您发现这个特性无法用TestCase表达，那么请将这个特性细
分，直至您可以为手上的特性写出TestCase为止。从这里开始是最安全的，它不会导致任何设计上重大的失误。但是，随着您不断的重构代码，不断的重构
TestCase，不断的依据TDD的思想做下去，最后当产品伴随测试用例集一起发布的时候，您就会不经意的发现经过重构以后的测试用例很可能是和产品中
的类/方法一一对应的。</p>
		<p>
				<b>[什么时候应该将全部测试都运行一遍？]</b>
				<br />Good
Question！大师们要求我们每次重构之后都要完整的运行一遍测试用例。这个要求可以理解，因为重构很可能会改变整个代码的结构或设计，从而导致不可
预见的后果，但是如果我正在开发的是一个ERP怎么办？运行一遍完整的测试用例可能将花费数个小时，况且现在很多重构都是由工具做到的，这个要求的可行性
和前提条件都有所动摇。所以我认为原则上你可以挑几个你觉得可能受到本次重构影响的TestCase去run，但是如果运行整个测试包只要花费数秒的时
间，那么不介意你按大师的要求去做。</p>
		<p>
				<b>[什么时候改进一个TestCase？]</b>
				<br />增加的测试用例或重构以后的代码导致了原来的TestCase的失去了效果，变得无
意义，甚至可能导致错误的结果，这时是改进TestCase的最好时机。但是有时你会发现，这样做仅仅导致了原来的TestCase在设计上是臃肿的，或
者是冗余的，这都不要紧，只要它没有失效，你仍然不用去改进它。记住，TestCase不是你的产品，它不要好看，也不要怎么太科学，甚至没有性能要求，
它只要能完成它的使命就可以了——这也证明了我们后面所说的“用Ctrl-C/Ctrl-V编写测试用例”的可行性。</p>
		<p>但是，美国人的想法其实跟我们还是不太一样，拿托尼巴赞的MindMap来说吧，其实画MindMap只是为了表现自己的思路，或记忆某些重要的事
情，但托尼却建议大家把MindMap画成一件艺术品，甚至还有很多艺术家把自己画的抽象派MindMap拿出来帮助托尼做宣传。同样，大师们也要求我们
把TestCase写的跟代码一样质量精良，可我想说的是，现在国内有几个公司能把产品的代码写的精良？？还是一步一步慢慢来吧。</p>
		<p>
				<b>[为什么原来通过的测试用例现在不能通过了？]</b>
				<br />这是一个警报，Red
Alert！它可能表达了两层意思——都不是什么好意思——1）你刚刚进行的重构可能失败了，或存在一些错误未被发现，至少重构的结果和原来的代码不等价
了。2）你刚刚增加的TestCase所表达的意思跟前面已经有的TestCase相冲突，也就是说，新增的功能违背了已有的设计，这种情况大部分可能是
之前的设计错了。但无论哪错了，无论是那层意思，想找到这个问题的根源都比TDD的正常工作要难。</p>
		<p>
				<b>[我怎么知道那里该有一个方法还是该有一个类？]</b>
				<br />这个问题也是常常出现在我的脑海中，无论你是第一次接触TDD或者已经成为
TDD专家，这个问题都会缠绕着你不放。不过问题的答案可以参考前面的“什么时候设计”一节，答案不是唯一的。其实多数时候你不必考虑未来，今天只做今天
的事，只要有重构工具，从方法到类和从类到方法都很容易。</p>
		<p>
				<b>[我要写一个TestCase，可是不知道从哪里开始？]</b>
				<br />从最重要的事开始，what matters
most？从脚下开始，从手头上的工作开始，从眼前的事开始。从一个没有UI的核心特性开始，从算法开始，或者从最有可能耽误时间的模块开始，从一个最严
重的bug开始。这是TDD主义者和鼠目寸光者的一个共同点，不同点是前者早已成竹在胸。</p>
		<p>
				<b>[为什么我的测试总是看起来有点愚蠢？]</b>
				<br />哦？是吗？来，握个手，我的也是！不必担心这一点，事实上，大师们给的例子也相当愚
蠢，比如一个极端的例子是要写一个两个int变量相加的方法，大师先断言2+3=5，再断言5+5=10，难道这些代码不是很愚蠢吗？其实这只是一个极端
的例子，当你初次接触TDD时，写这样的代码没什么不好，以后当你熟练时就会发现这样写没必要了，要记住，谦虚是通往TDD的必经之路！从经典开发方法转
向TDD就像从面向过程转向面向对象一样困难，你可能什么都懂，但你写出来的类没有一个纯OO的！我的同事还告诉我真正的太极拳，其速度是很快的，不比任
何一个快拳要慢，但是初学者（通常是指学习太极拳的前10年）太不容易把每个姿势都做对，所以只能慢慢来。</p>
		<p>
				<b>[什么场合不适用TDD？]</b>
				<br />问的好，确实有很多场合不适合使用TDD。比如对软件质量要求极高的军事或科研产品——神州六号，人命关天的软件——医疗设备，等等，再比如设计很重要必须提前做好的软件，这些都不适合TDD，但是不适合TDD不代表不能写TestCase，只是作用不同，地位不同罢了。</p>
		<p>
				<font color="#ff0000" size="5">{Best Practise}</font>
		</p>
		<p>
				<b>[微笑面对编译错误]</b>
				<br />学生时代最害怕的就是编译错误，编译错误可能会被老师视为上课不认真听课的证据，或者同学间相互嘲笑的
砝码。甚至离开学校很多年的老程序员依然害怕它就像害怕迟到一样，潜意识里似乎编译错误极有可能和工资挂钩（或者和智商挂钩，反正都不是什么好事）。其
实，只要提交到版本管理的代码没有编译错误就可以了，不要担心自己手上的代码的编译错误，通常，编译错误都集中在下面三个方面：<br />（1）你的代码存在低级错误<br />（2）由于某些Interface的实现尚不存在，所以被测试代码无法编译<br />（3）由于某些代码尚不存在，所以测试代码无法编译<br />请注意第二点与第三点完全不同，前者表明设计已存在，而实现不存在导致的编译错误；后者则指仅有TestCase而其它什么都没有的情况，设计和实现都不存在，没有Interface也没有Implementation。</p>
		<p>另外，编译器还有一个优点，那就是以最敏捷的身手告诉你，你的代码中有那些错误。当然如果你拥有Eclipse这样可以及时提示编译错误的IDE，就不需要这样的功能了。</p>
		<p>
				<b>[重视你的计划清单]</b>
				<br />在非TDD的情况下，尤其是传统的瀑布模型的情况下，程序员不会不知道该做什么，事实上，总是有设计或
者别的什么制品在引导程序员开发。但是在TDD的情况下，这种优势没有了，所以一个计划清单对你来说十分重要，因为你必须自己发现该做什么。不同性格的人
对于这一点会有不同的反应，我相信平时做事没什么计划要依靠别人安排的人（所谓将才）可能略有不适应，不过不要紧，Tasks和Calendar（又称效
率手册）早已成为现代上班族的必备工具了；而平时工作生活就很有计划性的人，比如我:)，就会更喜欢这种自己可以掌控Plan的方式了。</p>
		<p>
				<b>[废黜每日代码质量检查]</b>
				<br />如果我没有记错的话，PSP对于个人代码检查的要求是蛮严格的，而同样是在针对个人的问题上，
TDD却建议你废黜每日代码质量检查，别起疑心，因为你总是在做TestCase要求你做的事情，并且总是有办法（自动的）检查代码有没有做到这些事情
——红灯停绿灯行，所以每日代码检查的时间可能被节省，对于一个严格的PSP实践者来说，这个成本还是很可观的！</p>
		<p>此外，对于每日代码质量检查的另一个好处，就是帮助你认识自己的代码，全面的从宏观、微观、各个角度审视自己的成果，现在，当你依照TDD做事时，这个优点也不需要了，还记得前面说的TDD的第二个优点吗，因为你已经全面的使用了一遍你的代码，这完全可以达到目的。</p>
		<p>但是，问题往往也并不那么简单，现在有没有人能告诉我，我如何全面审视我所写的测试用例呢？别忘了，它们也是以代码的形式存在的哦。呵呵，但愿这个
问题没有把你吓到，因为我相信到目前为止，它还不是瓶颈问题，况且在编写产品代码的时候你还是会自主的发现很多测试代码上的没考虑到的地方，可以就此修改
一下。道理就是如此，世界上没有任何方法能代替你思考的过程，所以也没有任何方法能阻止你犯错误，TDD仅能让你更容易发现这些错误而已。</p>
		<p>
				<b>[如果无法完成一个大的测试，就从最小的开始]</b>
				<br />如果我无法开始怎么办，教科书上有个很好的例子：我要写一个电影列表的类，我
不知道如何下手，如何写测试用例，不要紧，首先想象静态的结果，如果我的电影列表刚刚建立呢，那么它应该是空的，OK，就写这个断言吧，断言一个刚刚初始
化的电影列表是空的。这不是愚蠢，这是细节，奥运会五项全能的金牌得主玛丽莲·金是这样说的：“成功人士的共同点在于……如果目标不够清晰，他们会首先做
通往成功道路上的每一个细小步骤……”。</p>
		<p>
				<b>[尝试编写自己的xUnit]</b>
				<br />Kent
Beck建议大家每当接触一个新的语言或开发平台的时候，就自己写这个语言或平台的xUnit，其实几乎所有常用的语言和平台都已经有了自己的
xUnit，而且都是大同小异，但是为什么大师给出了这样的建议呢。其实Kent
Beck的意思是说通过这样的方式你可以很快的了解这个语言或平台的特性，而且xUnit确实很简单，只要知道原理很快就能写出来。这对于那些喜欢自己写
底层代码的人，或者喜欢控制力的人而言是个好消息。</p>
		<p>
				<b>[善于使用Ctrl-C/Ctrl-V来编写TestCase]</b>
				<br />不必担心TestCase会有代码冗余的问题，让它冗余好了。</p>
		<p>
				<b>[永远都是功能First，改进可以稍后进行]</b>
				<br />上面这个标题还可以改成另外一句话：避免过渡设计！</p>
		<p>
				<b>[淘汰陈旧的用例]</b>
				<br />舍不得孩子套不着狼。不要可惜陈旧的用例，因为它们可能从概念上已经是错误的了，或仅仅会得出错误的结果，或者在某次重构之后失去了意义。当然也不一定非要删除它们，从TestSuite中除去（JUnit）或加上Ignored（NUnit）标签也是一个好办法。</p>
		<p>
				<b>[用TestCase做试验]</b>
				<br />如果你在开始某个特性或产品的开发之前对某个领域不太熟悉或一无所知，或者对自己在该领域里的
能力一无所知，那么你一定会选择做试验，在有单元测试作工具的情况下，建议你用TestCase做试验，这看起来就像你在写一个验证功能是否实现的
TestCase一样，而事实上也一样，只不过你所验证的不是代码本身，而是这些代码所依赖的环境。</p>
		<p>
				<b>[TestCase之间应该尽量独立]</b>
				<br />保证单独运行一个TestCase是有意义的。</p>
		<p>
				<b>[不仅测试必须要通过的代码，还要测试必须不能通过的代码]</b>
				<br />这是一个小技巧，也是不同于设计思路的东西。像越界的值或者乱
码，或者类型不符的变量，这些输入都可能会导致某个异常的抛出，或者导致一个标示“illegal
parameters”的返回值，这两种情况你都应该测试。当然我们无法枚举所有错误的输入或外部环境，这就像我们无法枚举所有正确的输入和外部环境一
样，只要TestCase能说明问题就可以了。</p>
		<p>
				<b>[编写代码的第一步，是在TestCase中用Ctrl-C]</b>
				<br />这是一个高级技巧，呃，是的，我是这个意思，我不是说这个技巧
难以掌握，而是说这个技巧当且仅当你已经是一个TDD高手时，你才能体会到它的魅力。多次使用TDD的人都有这样的体会，既然我的TestCase已经写
的很好了，很能说明问题，为什么我的代码不能从TestCase拷贝一些东西来呢。当然，这要求你的TestCase已经具有很好的表达能力，比如断言f
(5)=125的方式显然没有断言f(5)=5^(5-2)表达更多的内容。</p>
		<p>
				<b>[测试用例包应该尽量设计成可以自动运行的]</b>
				<br />如果产品是需要交付源代码的，那我们应该允许用户对代码进行修改或扩充后在自己
的环境下run整个测试用例包。既然通常情况下的产品是可以自动运行的，那为什么同样作为交付用户的制品，测试用例包就不是自动运行的呢？即使产品不需要
交付源代码，测试用例包也应该设计成可以自动运行的，这为测试部门或下一版本的开发人员提供了极大的便利。</p>
		<p>
				<b>[只亮一盏红灯]</b>
				<br />大师的建议，前面已经提到了，仅仅是建议。</p>
		<p>
				<b>[用TestCase描述你发现的bug]</b>
				<br />如果你在另一个部门的同事使用了你的代码，并且，他发现了一个bug，你猜他会怎
么做？他会立即走到你的工位边上，大声斥责说：“你有bug！”吗？如果他胆敢这样对你，对不起，你一定要冷静下来，不要当面回骂他，相反你可以微微一
笑，然后心平气和的对他说：“哦，是吗？那么好吧，给我一个TestCase证明一下。”现在局势已经倒向你这一边了，如果他还没有准备好回答你这致命的
一击，我猜他会感到非常羞愧，并在内心责怪自己太莽撞。事实上，如果他的TestCase没有过多的要求你的代码（而是按你们事前的契约），并且亮了红
灯，那么就可以确定是你的bug，反之，对方则无理了。用TestCase描述bug的另一个好处是，不会因为以后的修改而再次暴露这个bug，它已经成
为你发布每一个版本之前所必须检查的内容了。</p>
		<p>
				<font color="#ff0000" size="5">{关于单元测试}</font>
		</p>
		<p>单元测试的目标是</p>
		<blockquote>
				<p>
						<font style="font-weight: bold; font-size: large; background-color: rgb(220, 220, 220);">Keep the bar green to keep the code clean</font>
				</p>
		</blockquote>
		<p>这句话的含义是，事实上我们只做两件事情：让代码奏效（Keep the bar green）和让代码洁净（Keep the code clean），前者是把事情做对，后者是把事情做好，两者既是TDD中的两顶帽子，又是xUnit架构中的因果关系。</p>
		<p>单元测试作为软件测试的一个类别，并非是xUnit架构创造的，而是很早就有了。但是xUnit架构使得单元测试变得直接、简单、高效和规范，这也
是单元测试最近几年飞速发展成为衡量一个开发工具和环境的主要指标之一的原因。正如Martin
Fowler所说：“软件工程有史以来从没有如此众多的人大大收益于如此简单的代码！”而且多数语言和平台的xUnit架构都是大同小异，有的仅是语言不
同，其中最有代表性的是JUnit和NUnit，后者是前者的创新和扩展。一个单元测试框架xUnit应该：1）使每个TestCase独立运行；2）使
每个TestCase可以独立检测和报告错误；3）易于在每次运行之前选择TestCase。下面是我枚举出的xUnit框架的概念，这些概念构成了当前
业界单元测试理论和工具的核心：</p>
		<p>
				<b>[测试方法/TestMethod]</b>
				<br />测试的最小单位，直接表示为代码。</p>
		<p>
				<b>[测试用例/TestCase]</b>
				<br />由多个测试方法组成，是一个完整的对象，是很多TestRunner执行的最小单位。</p>
		<p>
				<b>[测试容器/TestSuite]</b>
				<br />由多个测试用例构成，意在把相同含义的测试用例手动安排在一起，TestSuite可以呈树状结构因而便于管理。在实现时，TestSuite形式上往往也是一个TestCase或TestFixture。</p>
		<p>
				<b>[断言/Assertion]</b>
				<br />断言一般有三类，分别是比较断言（如assertEquals），条件断言（如isTrue），和断言工具（如fail）。</p>
		<p>
				<b>[测试设备/TestFixture]</b>
				<br />为每个测试用例安排一个SetUp方法和一个TearDown方法，前者用于在执行该测试用例或该用例中的每个测试方法前调用以初始化某些内容，后者在执行该测试用例或该用例中的每个方法之后调用，通常用来消除测试对系统所做的修改。</p>
		<p>
				<b>[期望异常/Expected Exception]</b>
				<br />期望该测试方法抛出某种指定的异常，作为一个“断言”内容，同时也防止因为合情合理的异常而意外的终止了测试过程。</p>
		<p>
				<b>[种类/Category]</b>
				<br />为测试用例分类，实际使用时一般有TestSuite就不再使用Category，有Category就不再使用TestSuite。</p>
		<p>
				<b>[忽略/Ignored]</b>
				<br />设定该测试用例或测试方法被忽略，也就是不执行的意思。有些被抛弃的TestCase不愿删除，可以定为Ignored。</p>
		<p>
				<b>[测试执行器/TestRunner]</b>
				<br />执行测试的工具，表示以何种方式执行测试，别误会，这可不是在代码中规定的，完全是与测试内容无关的行为。比如文本方式，AWT方式，swing方式，或者Eclipse的一个视图等等。</p>
		<p>
				<font color="#ff0000" size="5">{实例：Fibonacci数列}</font>
		</p>
		<p>下面的Sample展示TDDer是如何编写一个旨在产生Fibonacci数列的方法。<br />（1）首先写一个TC，断言fib(1) = 1;fib(2) = 1;这表示该数列的第一个元素和第二个元素都是1。</p>
		<div style="border: 0.5pt solid windowtext; padding: 4px 5.4pt; background: rgb(230, 230, 230) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 98%;">
				<div>
						<span style="color: rgb(0, 0, 255);">public</span>
						<span style="color: rgb(0, 0, 0);"> </span>
						<span style="color: rgb(0, 0, 255);">void</span>
						<span style="color: rgb(0, 0, 0);"> testFab() {<br />        assertEquals(</span>
						<span style="color: rgb(0, 0, 0);">1</span>
						<span style="color: rgb(0, 0, 0);">, fib(</span>
						<span style="color: rgb(0, 0, 0);">1</span>
						<span style="color: rgb(0, 0, 0);">));<br />        assertEquals(</span>
						<span style="color: rgb(0, 0, 0);">1</span>
						<span style="color: rgb(0, 0, 0);">, fib(</span>
						<span style="color: rgb(0, 0, 0);">2</span>
						<span style="color: rgb(0, 0, 0);">));<br />}</span>
				</div>
		</div>
		<p>（2）上面这段代码不能编译通过，Great！——是的，我是说Great！当然，如果你正在用的是Eclipse那你不需要编译，Eclipse
会告诉你不存在fib方法，单击mark会问你要不要新建一个fib方法，Oh，当然！为了让上面那个TC能通过，我们这样写：</p>
		<div style="border: 0.5pt solid windowtext; padding: 4px 5.4pt; background: rgb(230, 230, 230) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 98%;">
				<div>
						<span style="color: rgb(0, 0, 255);">public</span>
						<span style="color: rgb(0, 0, 0);"> </span>
						<span style="color: rgb(0, 0, 255);">int</span>
						<span style="color: rgb(0, 0, 0);"> fib( </span>
						<span style="color: rgb(0, 0, 255);">int</span>
						<span style="color: rgb(0, 0, 0);"> n ) {<br />        </span>
						<span style="color: rgb(0, 0, 255);">return</span>
						<span style="color: rgb(0, 0, 0);"> </span>
						<span style="color: rgb(0, 0, 0);">1</span>
						<span style="color: rgb(0, 0, 0);">;<br />}</span>
				</div>
		</div>
		<p>（3）现在那个TC亮了绿灯，wow！应该庆祝一下了。接下来要增加TC的难度了，测第三个元素。</p>
		<div style="border: 0.5pt solid windowtext; padding: 4px 5.4pt; background: rgb(230, 230, 230) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 98%;">
				<div>
						<span style="color: rgb(0, 0, 255);">public</span>
						<span style="color: rgb(0, 0, 0);"> </span>
						<span style="color: rgb(0, 0, 255);">void</span>
						<span style="color: rgb(0, 0, 0);"> testFab() {<br />        assertEquals(</span>
						<span style="color: rgb(0, 0, 0);">1</span>
						<span style="color: rgb(0, 0, 0);">, fib(</span>
						<span style="color: rgb(0, 0, 0);">1</span>
						<span style="color: rgb(0, 0, 0);">));<br />        assertEquals(</span>
						<span style="color: rgb(0, 0, 0);">1</span>
						<span style="color: rgb(0, 0, 0);">, fib(</span>
						<span style="color: rgb(0, 0, 0);">2</span>
						<span style="color: rgb(0, 0, 0);">));<br />        assertEquals(</span>
						<span style="color: rgb(0, 0, 0);">2</span>
						<span style="color: rgb(0, 0, 0);">, fib(</span>
						<span style="color: rgb(0, 0, 0);">3</span>
						<span style="color: rgb(0, 0, 0);">));<br />}</span>
				</div>
		</div>
		<p>不过这样写还不太好看，不如这样写：</p>
		<div style="border: 0.5pt solid windowtext; padding: 4px 5.4pt; background: rgb(230, 230, 230) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 98%;">
				<div>
						<span style="color: rgb(0, 0, 255);">public</span>
						<span style="color: rgb(0, 0, 0);"> </span>
						<span style="color: rgb(0, 0, 255);">void</span>
						<span style="color: rgb(0, 0, 0);"> testFab() {<br />        assertEquals(</span>
						<span style="color: rgb(0, 0, 0);">1</span>
						<span style="color: rgb(0, 0, 0);">, fib(</span>
						<span style="color: rgb(0, 0, 0);">1</span>
						<span style="color: rgb(0, 0, 0);">));<br />        assertEquals(</span>
						<span style="color: rgb(0, 0, 0);">1</span>
						<span style="color: rgb(0, 0, 0);">, fib(</span>
						<span style="color: rgb(0, 0, 0);">2</span>
						<span style="color: rgb(0, 0, 0);">));<br />        assertEquals(fib(</span>
						<span style="color: rgb(0, 0, 0);">1</span>
						<span style="color: rgb(0, 0, 0);">)</span>
						<span style="color: rgb(0, 0, 0);">+</span>
						<span style="color: rgb(0, 0, 0);">fib(</span>
						<span style="color: rgb(0, 0, 0);">2</span>
						<span style="color: rgb(0, 0, 0);">), fib(</span>
						<span style="color: rgb(0, 0, 0);">3</span>
						<span style="color: rgb(0, 0, 0);">));<br />}</span>
				</div>
		</div>
		<p>（4）新增加的断言导致了红灯，为了扭转这一局势我们这样修改fib方法，其中部分代码是从上面的代码中Ctrl-C/Ctrl-V来的：</p>
		<div style="border: 0.5pt solid windowtext; padding: 4px 5.4pt; background: rgb(230, 230, 230) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 98%;">
				<div>
						<span style="color: rgb(0, 0, 255);">public</span>
						<span style="color: rgb(0, 0, 0);"> </span>
						<span style="color: rgb(0, 0, 255);">int</span>
						<span style="color: rgb(0, 0, 0);"> fib( </span>
						<span style="color: rgb(0, 0, 255);">int</span>
						<span style="color: rgb(0, 0, 0);"> n ) {<br />        </span>
						<span style="color: rgb(0, 0, 255);">if</span>
						<span style="color: rgb(0, 0, 0);"> ( n </span>
						<span style="color: rgb(0, 0, 0);">==</span>
						<span style="color: rgb(0, 0, 0);"> </span>
						<span style="color: rgb(0, 0, 0);">3</span>
						<span style="color: rgb(0, 0, 0);"> ) </span>
						<span style="color: rgb(0, 0, 255);">return</span>
						<span style="color: rgb(0, 0, 0);"> fib(</span>
						<span style="color: rgb(0, 0, 0);">1</span>
						<span style="color: rgb(0, 0, 0);">)</span>
						<span style="color: rgb(0, 0, 0);">+</span>
						<span style="color: rgb(0, 0, 0);">fib(</span>
						<span style="color: rgb(0, 0, 0);">2</span>
						<span style="color: rgb(0, 0, 0);">);<br />        </span>
						<span style="color: rgb(0, 0, 255);">return</span>
						<span style="color: rgb(0, 0, 0);"> </span>
						<span style="color: rgb(0, 0, 0);">1</span>
						<span style="color: rgb(0, 0, 0);">;<br />}</span>
				</div>
		</div>
		<p>（5）天哪，这真是个贱人写的代码！是啊，不是吗？因为TC就是产品的蓝本，产品只要恰好满足TC就ok。所以事情发展到这个地步不是fib方法的错，而是TC的错，于是TC还要进一步要求：</p>
		<div style="border: 0.5pt solid windowtext; padding: 4px 5.4pt; background: rgb(230, 230, 230) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 98%;">
				<div>
						<span style="color: rgb(0, 0, 255);">public</span>
						<span style="color: rgb(0, 0, 0);"> </span>
						<span style="color: rgb(0, 0, 255);">void</span>
						<span style="color: rgb(0, 0, 0);"> testFab() {<br />        assertEquals(</span>
						<span style="color: rgb(0, 0, 0);">1</span>
						<span style="color: rgb(0, 0, 0);">, fib(</span>
						<span style="color: rgb(0, 0, 0);">1</span>
						<span style="color: rgb(0, 0, 0);">));<br />        assertEquals(</span>
						<span style="color: rgb(0, 0, 0);">1</span>
						<span style="color: rgb(0, 0, 0);">, fib(</span>
						<span style="color: rgb(0, 0, 0);">2</span>
						<span style="color: rgb(0, 0, 0);">));<br />        assertEquals(fib(</span>
						<span style="color: rgb(0, 0, 0);">1</span>
						<span style="color: rgb(0, 0, 0);">)</span>
						<span style="color: rgb(0, 0, 0);">+</span>
						<span style="color: rgb(0, 0, 0);">fib(</span>
						<span style="color: rgb(0, 0, 0);">2</span>
						<span style="color: rgb(0, 0, 0);">), fib(</span>
						<span style="color: rgb(0, 0, 0);">3</span>
						<span style="color: rgb(0, 0, 0);">));<br />        assertEquals(fib(</span>
						<span style="color: rgb(0, 0, 0);">2</span>
						<span style="color: rgb(0, 0, 0);">)</span>
						<span style="color: rgb(0, 0, 0);">+</span>
						<span style="color: rgb(0, 0, 0);">fib(</span>
						<span style="color: rgb(0, 0, 0);">3</span>
						<span style="color: rgb(0, 0, 0);">), fib(</span>
						<span style="color: rgb(0, 0, 0);">4</span>
						<span style="color: rgb(0, 0, 0);">));<br />}</span>
				</div>
		</div>
		<p>（6）上有政策下有对策。</p>
		<div style="border: 0.5pt solid windowtext; padding: 4px 5.4pt; background: rgb(230, 230, 230) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 98%;">
				<div>
						<span style="color: rgb(0, 0, 255);">public</span>
						<span style="color: rgb(0, 0, 0);"> </span>
						<span style="color: rgb(0, 0, 255);">int</span>
						<span style="color: rgb(0, 0, 0);"> fib( </span>
						<span style="color: rgb(0, 0, 255);">int</span>
						<span style="color: rgb(0, 0, 0);"> n ) {<br />        </span>
						<span style="color: rgb(0, 0, 255);">if</span>
						<span style="color: rgb(0, 0, 0);"> ( n </span>
						<span style="color: rgb(0, 0, 0);">==</span>
						<span style="color: rgb(0, 0, 0);"> </span>
						<span style="color: rgb(0, 0, 0);">3</span>
						<span style="color: rgb(0, 0, 0);"> ) </span>
						<span style="color: rgb(0, 0, 255);">return</span>
						<span style="color: rgb(0, 0, 0);"> fib(</span>
						<span style="color: rgb(0, 0, 0);">1</span>
						<span style="color: rgb(0, 0, 0);">)</span>
						<span style="color: rgb(0, 0, 0);">+</span>
						<span style="color: rgb(0, 0, 0);">fib(</span>
						<span style="color: rgb(0, 0, 0);">2</span>
						<span style="color: rgb(0, 0, 0);">);<br />        </span>
						<span style="color: rgb(0, 0, 255);">if</span>
						<span style="color: rgb(0, 0, 0);"> ( n </span>
						<span style="color: rgb(0, 0, 0);">==</span>
						<span style="color: rgb(0, 0, 0);"> </span>
						<span style="color: rgb(0, 0, 0);">4</span>
						<span style="color: rgb(0, 0, 0);"> ) </span>
						<span style="color: rgb(0, 0, 255);">return</span>
						<span style="color: rgb(0, 0, 0);"> fib(</span>
						<span style="color: rgb(0, 0, 0);">2</span>
						<span style="color: rgb(0, 0, 0);">)</span>
						<span style="color: rgb(0, 0, 0);">+</span>
						<span style="color: rgb(0, 0, 0);">fib(</span>
						<span style="color: rgb(0, 0, 0);">3</span>
						<span style="color: rgb(0, 0, 0);">);<br />        </span>
						<span style="color: rgb(0, 0, 255);">return</span>
						<span style="color: rgb(0, 0, 0);"> </span>
						<span style="color: rgb(0, 0, 0);">1</span>
						<span style="color: rgb(0, 0, 0);">;<br />}</span>
				</div>
		</div>
		<p>（7）好了，不玩了。现在已经不是贱不贱的问题了，现在的问题是代码出现了冗余，所以我们要做的是——重构：</p>
		<div style="border: 0.5pt solid windowtext; padding: 4px 5.4pt; background: rgb(230, 230, 230) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 98%;">
				<div>
						<span style="color: rgb(0, 0, 255);">public</span>
						<span style="color: rgb(0, 0, 0);"> </span>
						<span style="color: rgb(0, 0, 255);">int</span>
						<span style="color: rgb(0, 0, 0);"> fib( </span>
						<span style="color: rgb(0, 0, 255);">int</span>
						<span style="color: rgb(0, 0, 0);"> n ) {<br />        </span>
						<span style="color: rgb(0, 0, 255);">if</span>
						<span style="color: rgb(0, 0, 0);"> ( n </span>
						<span style="color: rgb(0, 0, 0);">==</span>
						<span style="color: rgb(0, 0, 0);"> </span>
						<span style="color: rgb(0, 0, 0);">1</span>
						<span style="color: rgb(0, 0, 0);"> </span>
						<span style="color: rgb(0, 0, 0);">||</span>
						<span style="color: rgb(0, 0, 0);"> n </span>
						<span style="color: rgb(0, 0, 0);">==</span>
						<span style="color: rgb(0, 0, 0);"> </span>
						<span style="color: rgb(0, 0, 0);">2</span>
						<span style="color: rgb(0, 0, 0);"> ） </span>
						<span style="color: rgb(0, 0, 255);">return</span>
						<span style="color: rgb(0, 0, 0);"> </span>
						<span style="color: rgb(0, 0, 0);">1</span>
						<span style="color: rgb(0, 0, 0);">;<br />        </span>
						<span style="color: rgb(0, 0, 255);">else</span>
						<span style="color: rgb(0, 0, 0);"> </span>
						<span style="color: rgb(0, 0, 255);">return</span>
						<span style="color: rgb(0, 0, 0);"> fib( n </span>
						<span style="color: rgb(0, 0, 0);">-</span>
						<span style="color: rgb(0, 0, 0);"> </span>
						<span style="color: rgb(0, 0, 0);">1</span>
						<span style="color: rgb(0, 0, 0);"> ) </span>
						<span style="color: rgb(0, 0, 0);">+</span>
						<span style="color: rgb(0, 0, 0);"> fib( n </span>
						<span style="color: rgb(0, 0, 0);">-</span>
						<span style="color: rgb(0, 0, 0);"> </span>
						<span style="color: rgb(0, 0, 0);">2</span>
						<span style="color: rgb(0, 0, 0);"> );<br />}</span>
				</div>
		</div>
		<p>（8）好，现在你已经fib方法已经写完了吗？错了，一个危险的错误，你忘了错误的输入了。我们令0表示Fibonacci中没有这一项。</p>
		<div style="border: 0.5pt solid windowtext; padding: 4px 5.4pt; background: rgb(230, 230, 230) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 98%;">
				<div>
						<span style="color: rgb(0, 0, 255);">public</span>
						<span style="color: rgb(0, 0, 0);"> </span>
						<span style="color: rgb(0, 0, 255);">void</span>
						<span style="color: rgb(0, 0, 0);"> testFab() {<br />        assertEquals(</span>
						<span style="color: rgb(0, 0, 0);">1</span>
						<span style="color: rgb(0, 0, 0);">, fib(</span>
						<span style="color: rgb(0, 0, 0);">1</span>
						<span style="color: rgb(0, 0, 0);">));<br />        assertEquals(</span>
						<span style="color: rgb(0, 0, 0);">1</span>
						<span style="color: rgb(0, 0, 0);">, fib(</span>
						<span style="color: rgb(0, 0, 0);">2</span>
						<span style="color: rgb(0, 0, 0);">));<br />        assertEquals(fib(</span>
						<span style="color: rgb(0, 0, 0);">1</span>
						<span style="color: rgb(0, 0, 0);">)</span>
						<span style="color: rgb(0, 0, 0);">+</span>
						<span style="color: rgb(0, 0, 0);">fib(</span>
						<span style="color: rgb(0, 0, 0);">2</span>
						<span style="color: rgb(0, 0, 0);">), fib(</span>
						<span style="color: rgb(0, 0, 0);">3</span>
						<span style="color: rgb(0, 0, 0);">));<br />        assertEquals(fib(</span>
						<span style="color: rgb(0, 0, 0);">2</span>
						<span style="color: rgb(0, 0, 0);">)</span>
						<span style="color: rgb(0, 0, 0);">+</span>
						<span style="color: rgb(0, 0, 0);">fib(</span>
						<span style="color: rgb(0, 0, 0);">3</span>
						<span style="color: rgb(0, 0, 0);">), fib(</span>
						<span style="color: rgb(0, 0, 0);">4</span>
						<span style="color: rgb(0, 0, 0);">));<br />        assertEquals(</span>
						<span style="color: rgb(0, 0, 0);">0</span>
						<span style="color: rgb(0, 0, 0);">, fib(</span>
						<span style="color: rgb(0, 0, 0);">0</span>
						<span style="color: rgb(0, 0, 0);">));<br />        assertEquals(</span>
						<span style="color: rgb(0, 0, 0);">0</span>
						<span style="color: rgb(0, 0, 0);">, fib(</span>
						<span style="color: rgb(0, 0, 0);">-</span>
						<span style="color: rgb(0, 0, 0);">1</span>
						<span style="color: rgb(0, 0, 0);">));<br />}</span>
				</div>
		</div>
		<p>then change the method fib to make the bar grean：</p>
		<div style="border: 0.5pt solid windowtext; padding: 4px 5.4pt; background: rgb(230, 230, 230) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 98%;">
				<div>
						<span style="color: rgb(0, 0, 255);">public</span>
						<span style="color: rgb(0, 0, 0);"> </span>
						<span style="color: rgb(0, 0, 255);">int</span>
						<span style="color: rgb(0, 0, 0);"> fib( </span>
						<span style="color: rgb(0, 0, 255);">int</span>
						<span style="color: rgb(0, 0, 0);"> n ) {<br />        </span>
						<span style="color: rgb(0, 0, 255);">if</span>
						<span style="color: rgb(0, 0, 0);"> ( n </span>
						<span style="color: rgb(0, 0, 0);">&lt;=</span>
						<span style="color: rgb(0, 0, 0);"> </span>
						<span style="color: rgb(0, 0, 0);">0</span>
						<span style="color: rgb(0, 0, 0);"> ) </span>
						<span style="color: rgb(0, 0, 255);">return</span>
						<span style="color: rgb(0, 0, 0);"> </span>
						<span style="color: rgb(0, 0, 0);">0</span>
						<span style="color: rgb(0, 0, 0);">;<br />        </span>
						<span style="color: rgb(0, 0, 255);">if</span>
						<span style="color: rgb(0, 0, 0);"> ( n </span>
						<span style="color: rgb(0, 0, 0);">==</span>
						<span style="color: rgb(0, 0, 0);"> </span>
						<span style="color: rgb(0, 0, 0);">1</span>
						<span style="color: rgb(0, 0, 0);"> </span>
						<span style="color: rgb(0, 0, 0);">||</span>
						<span style="color: rgb(0, 0, 0);"> n </span>
						<span style="color: rgb(0, 0, 0);">==</span>
						<span style="color: rgb(0, 0, 0);"> </span>
						<span style="color: rgb(0, 0, 0);">2</span>
						<span style="color: rgb(0, 0, 0);"> ） </span>
						<span style="color: rgb(0, 0, 255);">return</span>
						<span style="color: rgb(0, 0, 0);"> </span>
						<span style="color: rgb(0, 0, 0);">1</span>
						<span style="color: rgb(0, 0, 0);">;<br />        </span>
						<span style="color: rgb(0, 0, 255);">else</span>
						<span style="color: rgb(0, 0, 0);"> </span>
						<span style="color: rgb(0, 0, 255);">return</span>
						<span style="color: rgb(0, 0, 0);"> fib( n </span>
						<span style="color: rgb(0, 0, 0);">-</span>
						<span style="color: rgb(0, 0, 0);"> </span>
						<span style="color: rgb(0, 0, 0);">1</span>
						<span style="color: rgb(0, 0, 0);"> ) </span>
						<span style="color: rgb(0, 0, 0);">+</span>
						<span style="color: rgb(0, 0, 0);"> fib( n </span>
						<span style="color: rgb(0, 0, 0);">-</span>
						<span style="color: rgb(0, 0, 0);"> </span>
						<span style="color: rgb(0, 0, 0);">2</span>
						<span style="color: rgb(0, 0, 0);"> );<br />}</span>
				</div>
		</div>
		<p>（9）下班前最后一件事情，把TC也重构一下：</p>
		<div style="border: 0.5pt solid windowtext; padding: 4px 5.4pt; background: rgb(230, 230, 230) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 98%;">
				<div>
						<span style="color: rgb(0, 0, 255);">public</span>
						<span style="color: rgb(0, 0, 0);"> </span>
						<span style="color: rgb(0, 0, 255);">void</span>
						<span style="color: rgb(0, 0, 0);"> testFab() {<br />        </span>
						<span style="color: rgb(0, 0, 255);">int</span>
						<span style="color: rgb(0, 0, 0);"> cases[][] </span>
						<span style="color: rgb(0, 0, 0);">=</span>
						<span style="color: rgb(0, 0, 0);"> {<br />                {</span>
						<span style="color: rgb(0, 0, 0);">0</span>
						<span style="color: rgb(0, 0, 0);">, </span>
						<span style="color: rgb(0, 0, 0);">0</span>
						<span style="color: rgb(0, 0, 0);">}, {</span>
						<span style="color: rgb(0, 0, 0);">-</span>
						<span style="color: rgb(0, 0, 0);">1</span>
						<span style="color: rgb(0, 0, 0);">, </span>
						<span style="color: rgb(0, 0, 0);">0</span>
						<span style="color: rgb(0, 0, 0);">},  </span>
						<span style="color: rgb(0, 128, 0);">//</span>
						<span style="color: rgb(0, 128, 0);">the wrong parameters</span>
						<span style="color: rgb(0, 128, 0);">
								<br />
						</span>
						<span style="color: rgb(0, 0, 0);">                {</span>
						<span style="color: rgb(0, 0, 0);">1</span>
						<span style="color: rgb(0, 0, 0);">, </span>
						<span style="color: rgb(0, 0, 0);">1</span>
						<span style="color: rgb(0, 0, 0);">}, {</span>
						<span style="color: rgb(0, 0, 0);">2</span>
						<span style="color: rgb(0, 0, 0);">, </span>
						<span style="color: rgb(0, 0, 0);">1</span>
						<span style="color: rgb(0, 0, 0);">}};  </span>
						<span style="color: rgb(0, 128, 0);">//</span>
						<span style="color: rgb(0, 128, 0);">the first 2 elements</span>
						<span style="color: rgb(0, 128, 0);">
								<br />
						</span>
						<span style="color: rgb(0, 0, 0);">
								<br />        </span>
						<span style="color: rgb(0, 0, 255);">for</span>
						<span style="color: rgb(0, 0, 0);"> (</span>
						<span style="color: rgb(0, 0, 255);">int</span>
						<span style="color: rgb(0, 0, 0);"> i </span>
						<span style="color: rgb(0, 0, 0);">=</span>
						<span style="color: rgb(0, 0, 0);"> </span>
						<span style="color: rgb(0, 0, 0);">0</span>
						<span style="color: rgb(0, 0, 0);">; i </span>
						<span style="color: rgb(0, 0, 0);">&lt;</span>
						<span style="color: rgb(0, 0, 0);"> cases.length; i</span>
						<span style="color: rgb(0, 0, 0);">++</span>
						<span style="color: rgb(0, 0, 0);">)<br />                assertEquals( cases[i][</span>
						<span style="color: rgb(0, 0, 0);">1</span>
						<span style="color: rgb(0, 0, 0);">], fib(cases[i][</span>
						<span style="color: rgb(0, 0, 0);">0</span>
						<span style="color: rgb(0, 0, 0);">]) );<br /><br />        </span>
						<span style="color: rgb(0, 128, 0);">//</span>
						<span style="color: rgb(0, 128, 0);">the rest elements</span>
						<span style="color: rgb(0, 128, 0);">
								<br />
						</span>
						<span style="color: rgb(0, 0, 0);">        </span>
						<span style="color: rgb(0, 0, 255);">for</span>
						<span style="color: rgb(0, 0, 0);"> (</span>
						<span style="color: rgb(0, 0, 255);">int</span>
						<span style="color: rgb(0, 0, 0);"> i </span>
						<span style="color: rgb(0, 0, 0);">=</span>
						<span style="color: rgb(0, 0, 0);"> </span>
						<span style="color: rgb(0, 0, 0);">3</span>
						<span style="color: rgb(0, 0, 0);">; i </span>
						<span style="color: rgb(0, 0, 0);">&lt;</span>
						<span style="color: rgb(0, 0, 0);"> </span>
						<span style="color: rgb(0, 0, 0);">20</span>
						<span style="color: rgb(0, 0, 0);">; i</span>
						<span style="color: rgb(0, 0, 0);">++</span>
						<span style="color: rgb(0, 0, 0);">)<br />                assertEquals(fib(i</span>
						<span style="color: rgb(0, 0, 0);">-</span>
						<span style="color: rgb(0, 0, 0);">1</span>
						<span style="color: rgb(0, 0, 0);">)</span>
						<span style="color: rgb(0, 0, 0);">+</span>
						<span style="color: rgb(0, 0, 0);">fib(i</span>
						<span style="color: rgb(0, 0, 0);">-</span>
						<span style="color: rgb(0, 0, 0);">2</span>
						<span style="color: rgb(0, 0, 0);">), fib(i));<br />}</span>
				</div>
		</div>
		<p>（10）打完收工。</p>
		<p>
				<font color="#ff0000" size="5">{关于本文的写作}</font>
		</p>
		<p>在本文的写作过程中，作者也用到了TDD的思维，事实上作者先构思要写一篇什么样的文章，然后写出这篇文章应该满足的几个要求，包括功能的要求（要
写些什么）和性能的要求（可读性如何）和质量的要求（文字的要求），这些要求起初是一个也达不到的（因为正文还一个字没有），在这种情况下作者的文章无法
编译通过，为了达到这些要求，作者不停的写啊写啊，终于在花尽了两个月的心血之后完成了当初既定的所有要求（make the bar
green），随后作者整理了一下文章的结构（重构），在满意的提交给了Blog系统之后，作者穿上了一件绿色的汗衫，趴在地上，学了两声青蛙
叫。。。。。。。^_^</p>
		<p>
				<font color="#ff0000" size="5">{后记：Martin Fowler在中国}</font>
		</p>
		<p>从本文正式完成到发表的几个小时里，我偶然读到了Martin Fowler先生北京访谈录，其间提到了很多对测试驱动开发的看法，摘抄在此：</p>
		<blockquote>
				<p>
						<font color="#008000">Martin Fowler：当然（值得花一半的时间来写单元测试）！因为单元测试能够使你更快的完成工作。无数次的实践已经证明这一点。你的时间越是紧张，就越要写单元测试，它看上去慢，但实际上能够帮助你更快、更舒服地达到目的。<br />Martin Fowler：什么叫重要？什么叫不重要？这是需要逐渐认识的，不是想当然的。我为绝大多数的模块写单元测试，是有点烦人，但是当你意识到这工作的价值时，你会欣然的。<br />Martin Fowler：对全世界的程序员我都是那么几条建议：……第二，学习测试驱动开发，这种新的方法会改变你对于软件开发的看法。……</font>
				</p>
				<p align="right">——《程序员》，2005年7月刊</p>
		</blockquote>
		<p>
				<font color="#ff0000" size="5">{鸣谢}</font>
		</p>
		<p>
				<a href="http://www.blogjava.net/fhawk">fhawk</a>
				<br />Dennis Chen<br /><a href="http://xdingding.cnblogs.com/">般若菩提</a><br /><a href="http://c2.com/ppr/about/author/kent.html">Kent Beck</a><br /><a href="http://www.martinfowler.com/">Martin Fowler</a><br /><a href="http://c2.com/">c2.com</a></p>
<img src ="http://www.cnitblog.com/martin/aggbug/9813.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/martin/" target="_blank">Martin</a> 2006-04-27 19:51 <a href="http://www.cnitblog.com/martin/archive/2006/04/27/9813.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]我对系统重构的理解</title><link>http://www.cnitblog.com/martin/archive/2006/04/27/9810.html</link><dc:creator>Martin</dc:creator><author>Martin</author><pubDate>Thu, 27 Apr 2006 11:32:00 GMT</pubDate><guid>http://www.cnitblog.com/martin/archive/2006/04/27/9810.html</guid><wfw:comment>http://www.cnitblog.com/martin/comments/9810.html</wfw:comment><comments>http://www.cnitblog.com/martin/archive/2006/04/27/9810.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/martin/comments/commentRss/9810.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/martin/services/trackbacks/9810.html</trackback:ping><description><![CDATA[
		<strong>From: http://www.cnblogs.com/lane_cn/archive/2006/02/05/325782.html<br /><br />什么是重构<br /></strong>
		<br />重构，用最简单的一句话说：就是要在不改变系统功能的情况下，对系统的内部结构进行重新调整。重构的最直接目的在于改进软件系统的内部架构。一个好的结构可以更加适应于需求的变化，更好的满足客户的需求，最大限度的延长软件系统的生命周期。<br /><strong><br />为什么要重构<br /></strong><br />在不改变系统功能的情况下，改变系统的实现方式。为什么要这么做？投入精力不用来满足客户关心的需求，而是仅仅改变了软件的实现方式，这是否是在浪费客户的投资呢？<br /><br />重构的重要性要从软件的生命周期说起。软件不同与普通的产品，他是一种智力产品，没有具体的物理形态。一个软件不可能发生物理损耗，界面上的按钮永远不会因为按动次数太多而发生接触不良。那么为什么一个软件制造出来以后，却不能永远使用下去呢？<br /><br />对软件的生命造成威胁的因素只有一个：需求的变更。一个软件总是为解决某种特定的需求而产生，时代在发展，客户的业务也在发生变化。有的需求相对稳定一些，有的需求变化的比较剧烈，还有的需求已经消失了，或者转化成了别的需求。在这种情况下，软件必须相应的改变。<br /><br />考虑到成本和时间等因素，当然不是所有的需求变化都要在软件系统中实现。但是总的说来，软件要适应需求的变化，以保持自己的生命力。<br /><br />这
就产生了一种糟糕的现象：软件产品最初制造出来，是经过精心的设计，具有良好架构的。但是随着时间的发展、需求的变化，必须不断的修改原有的功能、追加新
的功能，还免不了有一些缺陷需要修改。为了实现变更，不可避免的要违反最初的设计构架。经过一段时间以后，软件的架构就千疮百孔了。bug越来越多，越来
越难维护，新的需求越来越难实现，软件的构架对新的需求渐渐的失去支持能力，而是成为一种制约。最后新需求的开发成本会超过开发一个新的软件的成本，这就
是这个软件系统的生命走到尽头的时候。<br /><br />重构就能够最大限度的避免这样一种现象。系统发展到一定阶段后，使用重构的方式，不改变系统的外部功能，只对内部的结构进行重新的整理。通过重构，不断的调整系统的结构，使系统对于需求的变更始终具有较强的适应能力。<br /><strong><br />拒绝变化 VS 拥抱变化<br /></strong><br />按照传统的软件设计方式，软件的生产分为需求调查、概要设计、详细设计、编码、单体测试、联合测试、现场部署几个阶段。虽说这几个阶段是可以互相渗透，但是总的来说是有一定次序的，前一个阶段的工作是后一个阶段工作的基础。这就向下面这样一种V形的模式：<br /><br /><img src="http://www.cnitblog.com/images/cnitblog_com/martin/vdevelope.GIF" alt="vdevelope.GIF" border="0" height="245" width="368" /><br /><br />往
下的方向将系统进行分解，往上的方向将系统进行整合。这样的开发形式将软件开发分为设计前和设计后两个阶段，开发过程中存在一个重要的“里程碑”——设计
说明书的。在设计说明书完成前，工程处于“设计”阶段，而在设计说明书完成之后，工程则进入“实施”阶段。一旦到了实施阶段，任何需求或者设计上的变更都
是非常困难的，需要花费大量的成本。通常为了保证工程的顺利实施，开发人员常有这样一种冲动：按住客户的手，在需求说明书上签字。并且告诉客户：“从今天
开始，任何需求变更都要停止，直到我们把现在这个东西做完。”这是一种拒绝变化的开发方式。<br /><br />软件系统要保持与企业的目标一致。时代在发展，人们的要求在不断的提高，客户的业务在不断的发展。在这种情况下，传统的先设计、再施工的V形式已经不能适应日益复杂的业务需要。软件工程逐渐演化成下面这样的过程：<br /><br /><img src="http://www.cnitblog.com/images/cnitblog_com/martin/sdevelope.GIF" alt="sdevelope.GIF" border="0" height="160" width="163" /><br /><br />说明一下：<br />1、软件开发的目标要与企业目标保持一致，一个开发周期不宜时间过长，一般控制在半年到一年。系统部署后，并不意味着开发工作结束了，而是进入了下一个周期。<br /><br />2、
工程以循环迭代的方式前进，这并不意味轻视了设计，不是要搞边调研、边设计、边施工的“三边”工程，相反，是更加重视设计的地位。软件开发的全过程都需要
设计，软件开发是“持续设计”的过程。同时，设计工作也不只是简单过程分解、任务分配，而是概念设计、逻辑设计、物理设计等各个方面互相交织、齐头并进。<br /><br />传
统的软件开发方式使用一种非常理想化的流程——先与客户讨论项目的范围，确定哪些需要做，哪些不需要做，然后规划一个完美的设计，不仅可以满足现在的需
求，还能很好的适应未来的需求，设计完成后开始编码，然后测试组装，送到现场安装调试运行。这一系列过程就类似与发射一颗炮弹，首先要找到目标，然后根据
地形、风力、目标的位置、移动速度等各种因素，计算提前量、炮弹发射的角度，计算出一个抛物线轨道，最后在合适的时间把炮弹发射出去。这一切都符合最正确
的物理定律，一切都听起来很理想。如果没有意外条件，当然是可以击中目标的。但是炮弹一旦发射出去，一切就失去了控制，任何环境的变化都会造成偏离目标。
尤其是对于一个运动的目标来说，计算过程十分复杂，很多情况下只能靠人估计。对于不规则的运动目标只能碰碰运气。这样的方式，命中率是很低的。<br /><br />新
的软件开发过程不追求完美的、长期的、理想的计划，更加重视实际情况，重视需求的变化，提倡采用短期的计划。这是一种拥抱变化的过程。就象是在炮弹上安装
了一个反馈装置，锁定目标后，确保大方向的正确，然后就将炮弹发射出去。炮弹在运行过程中不断的将目标位置偏移量输入反馈电路，根据反馈输出调整自己的运
行路线，无限的逼近目标。这样，炮弹就拥有了制导能力，命中率大大增加。<br /><br />重构就可以增加工程的调整能力，他可以把产品回复到一个稳定的状态，可以基于这个状态达到下一个目标。如此反复前进，更好的满足客户的需求。<br /><br /><strong>保持兼容性<br /></strong><br />重构的目的在于改变系统的实现方式，而不改变原有的功能。这个过程中，判断兼容性就十分的重要。一个子系统、模块、类、函数是否与升级前保持兼容，如何判断这个兼容性，如何保持这个兼容性，这关系到重构的成本和重构的可能性。<br /><br />程
序员学习写程序代码时，会发现随着程序代码愈来愈多，许多的程序代码不断重复出现和被使用，因此很自然的开始使用例程、子程序或是过程、函数等机制帮助我
们进行程序代码整理的工作。于是很自然的，字体的分析方式演化成这个样子：将客户的需求过程进行分解，一步一步的分解，直到可以直接的实现他。这就是面向
过程的分析方式。<br /><br />面向过程的分析方式对变化的能力是很弱的。为什么呢？因为面向过程的分析方式很容易造成一种倾向——不区分行动的主体。
一个过程是没有主体的，他不是在为自己工作，而是在为“别人”工作。当我们修改了一个过程之后，我们很难判断这个过程是否保持向后兼容，其他过程会不会受
到影响。因为这个过程对外界有意义的不仅是他的输入和输出，还包括每一步过程，每一步过程都可能含有一个非常隐讳的业务意义，对外界产生影响。<br /><br />因此，修改一个过程是非常困难的，通常升级一个面向过程的系统，可以采用两种方式：<br />1、写新的过程；<br />2、在原有的过程上加开关参数。<br /><br />除此以外的升级办法都很难保证原过程和新过程的兼容性，容易造成错误。<br /><br />为了更好的保证升级后模块的兼容性，应该采用面向对象的分析方式。按照这样的分析方式，一个对象为“自己”工作，他有完整的、独立的业务含义。对象之间通过接口发生联系，一个对象对外界有影响的部分只有接口，至于他做什么、如何做、做的对不对，则不是外界需要关心的事情。<br /><br />判断一个接口升级后是否保持兼容性就是一件比较容易的事情了。我们可以判断接口的输入输出是否符合下面两条规则：<br />1、升级后的输入是升级前的输入的超级；<br />2、升级后的输出是升级前的输出的子集。<br /><br />只要符合这两点，他就仍然可以在系统中运行，不会对其他对象造成危害。在实际的工程中，判断这个兼容性有一个更好的办法：<strong>自动化的单元测试</strong>。<br /><br />在重构的过程中，自动化的单元测试是非常好的保障。采用自动化的单元测试，不断运行测试，可以保证系统的结构改变的过程中，业务行为不发生改变。<img src ="http://www.cnitblog.com/martin/aggbug/9810.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/martin/" target="_blank">Martin</a> 2006-04-27 19:32 <a href="http://www.cnitblog.com/martin/archive/2006/04/27/9810.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>（转贴）追MM与Java的23种设计模式</title><link>http://www.cnitblog.com/martin/archive/2006/04/14/9173.html</link><dc:creator>Martin</dc:creator><author>Martin</author><pubDate>Fri, 14 Apr 2006 03:41:00 GMT</pubDate><guid>http://www.cnitblog.com/martin/archive/2006/04/14/9173.html</guid><wfw:comment>http://www.cnitblog.com/martin/comments/9173.html</wfw:comment><comments>http://www.cnitblog.com/martin/archive/2006/04/14/9173.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/martin/comments/commentRss/9173.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/martin/services/trackbacks/9173.html</trackback:ping><description><![CDATA[
		<div class="postTitle">
				<a id="viewpost1_TitleUrl" class="postTitle2" href="../../gavntery/archive/2005/12/18/5541.html">（转贴）追MM与Java的23种设计模式</a>
		</div>
		<p>    From: http://www.cnitblog.com/gavntery/archive/2005/12/18/5541.html<br /></p>
		<p>　　我在Java论坛看到这篇文章，作者以轻松的语言比喻了java的32种模式，有很好的启发作用，但可惜没有给出具体的意思，我就在后边加上了。这些都是最简单的介绍，要学习的话建议你看一下阎宏博士的《Java与模式》一书。 </p>
		<p>
				<strong>创建型模式</strong>
		</p>
		<p>1、FACTORY―追MM少不了请吃饭了，麦当劳的鸡翅和肯德基的鸡翅都是MM爱吃的东西，虽然口味有所不同，但不管你带MM去麦当劳或肯德基，只管向服务员说“来四个鸡翅”就行了。麦当劳和肯德基就是生产鸡翅的Factory <br /><br />　　工厂模式：客户类和工厂类分开。消费者任何时候需要某种产品，只需向工厂请求即可。消费者无须修改就可以接纳新产品。缺点是当产品修改时，工厂类也要做相应的修改。如：如何创建及如何向客户端提供。 <br /><br />　
　2、BUILDER―MM最爱听的就是“我爱你”这句话了，见到不同地方的MM,要能够用她们的方言跟她说这句话哦，我有一个多种语言翻译机，上面每种
语言都有一个按键，见到MM我只要按对应的键，它就能够用相应的语言说出“我爱你”这句话了，国外的MM也可以轻松搞掂，这就是我的“我爱你”
builder。（这一定比美军在伊拉克用的翻译机好卖） <br /><br />　　建造模式：将产品的内部表象和产品的生成过程分割开来，从而使一个建造过程生成具有不同的内部表象的产品对象。建造模式使得产品内部表象可以独立的变化，客户不必知道产品内部组成的细节。建造模式可以强制实行一种分步骤进行的建造过程。 <br /><br />　　3、FACTORY METHOD―请MM去麦当劳吃汉堡，不同的MM有不同的口味，要每个都记住是一件烦人的事情，我一般采用Factory Method模式，带着MM到服务员那儿，说“要一个汉堡”，具体要什么样的汉堡呢，让MM直接跟服务员说就行了。 <br /><br />　　工厂方法模式：核心工厂类不再负责所有产品的创建，而是将具体创建的工作交给子类去做，成为一个抽象工厂角色，仅负责给出具体工厂类必须实现的接口，而不接触哪一个产品类应当被实例化这种细节。 <br /><br />　　4、PROTOTYPE―跟MM用QQ聊天，一定要说些深情的话语了，我搜集了好多肉麻的情话，需要时只要copy出来放到QQ里面就行了，这就是我的情话prototype了。（100块钱一份，你要不要） <br /><br />　
　原始模型模式：通过给出一个原型对象来指明所要创建的对象的类型，然后用复制这个原型对象的方法创建出更多同类型的对象。原始模型模式允许动态的增加或
减少产品类，产品类不需要非得有任何事先确定的等级结构，原始模型模式适用于任何的等级结构。缺点是每一个类都必须配备一个克隆方法。 <br /><br />　　5、SINGLETON―俺有6个漂亮的老婆，她们的老公都是我，我就是我们家里的老公Sigleton，她们只要说道“老公”，都是指的同一个人，那就是我(刚才做了个梦啦，哪有这么好的事) <br /><br />　　单例模式：单例模式确保某一个类只有一个实例，而且自行实例化并向整个系统提供这个实例单例模式。单例模式只应在有真正的“单一实例”的需求时才可使用。 <br /><br /><strong>结构型模式</strong><br /><br />　　6、ADAPTER―在朋友聚会上碰到了一个美女Sarah，从香港来的，可我不会说粤语，她不会说普通话，只好求助于我的朋友kent了，他作为我和Sarah之间的Adapter，让我和Sarah可以相互交谈了(也不知道他会不会耍我) <br /><br />　　适配器（变压器）模式：把一个类的接口变换成客户端所期待的另一种接口，从而使原本因接口原因不匹配而无法一起工作的两个类能够一起工作。适配类可以根据参数返还一个合适的实例给客户端。 <br /><br />　　7、BRIDGE―早上碰到MM，要说早上好，晚上碰到MM，要说晚上好；碰到MM穿了件新衣服，要说你的衣服好漂亮哦，碰到MM新做的发型，要说你的头发好漂亮哦。不要问我“早上碰到MM新做了个发型怎么说”这种问题，自己用BRIDGE组合一下不就行了 <br /><br />　　桥梁模式：将抽象化与实现化脱耦，使得二者可以独立的变化，也就是说将他们之间的强关联变成弱关联，也就是指在一个软件系统的抽象化和实现化之间使用组合/聚合关系而不是继承关系，从而使两者可以独立的变化。 <br /><br />　
　8、COMPOSITE―Mary今天过生日。“我过生日，你要送我一件礼物。”“嗯，好吧，去商店，你自己挑。”“这件T恤挺漂亮，买，这条裙子好
看，买，这个包也不错，买。”“喂，买了三件了呀，我只答应送一件礼物的哦。”“什么呀，T恤加裙子加包包，正好配成一套呀，小姐，麻烦你包起来。”
“……”，MM都会用Composite模式了，你会了没有？ <br /><br />　　合成模式：合成模式将对象组织到树结构中，可以用来描述整体与部分的关系。合成模式就是一个处理对象的树结构的模式。合成模式把部分与整体的关系用树结构表示出来。合成模式使得客户端把一个个单独的成分对象和由他们复合而成的合成对象同等看待。 <br /><br />　
　9、DECORATOR―Mary过完轮到Sarly过生日，还是不要叫她自己挑了，不然这个月伙食费肯定玩完，拿出我去年在华山顶上照的照片，在背面
写上“最好的的礼物，就是爱你的Fita”，再到街上礼品店买了个像框（卖礼品的MM也很漂亮哦），再找隔壁搞美术设计的Mike设计了一个漂亮的盒子装
起来……，我们都是Decorator，最终都在修饰我这个人呀，怎么样，看懂了吗？ <br /><br />　　装饰模式：装饰模式以对客户端透明的方式扩展对象的功能，是继承关系的一个替代方案，提供比继承更多的灵活性。动态给一个对象增加功能，这些功能可以再动态的撤消。增加由一些基本功能的排列组合而产生的非常大量的功能。 <br /><br />　
　10、FACADE―我有一个专业的Nikon相机，我就喜欢自己手动调光圈、快门，这样照出来的照片才专业，但MM可不懂这些，教了半天也不会。幸好
相机有Facade设计模式，把相机调整到自动档，只要对准目标按快门就行了，一切由相机自动调整，这样MM也可以用这个相机给我拍张照片了。 <br /><br />　　门面模式：外部与一个子系统的通信必须通过一个统一的门面对象进行。门面模式提供一个高层次的接口，使得子系统更易于使用。每一个子系统只有一个门面类，而且此门面类只有一个实例，也就是说它是一个单例模式。但整个系统可以有多个门面类。 <br /><br />　
　11、FLYWEIGHT―每天跟MM发短信，手指都累死了，最近买了个新手机，可以把一些常用的句子存在手机里，要用的时候，直接拿出来，在前面加上
MM的名字就可以发送了，再不用一个字一个字敲了。共享的句子就是Flyweight，MM的名字就是提取出来的外部特征，根据上下文情况使用。 <br /><br />　
　享元模式：FLYWEIGHT在拳击比赛中指最轻量级。享元模式以共享的方式高效的支持大量的细粒度对象。享元模式能做到共享的关键是区分内蕴状态和外
蕴状态。内蕴状态存储在享元内部，不会随环境的改变而有所不同。外蕴状态是随环境的改变而改变的。外蕴状态不能影响内蕴状态，它们是相互独立的。将可以共
享的状态和不可以共享的状态从常规类中区分开来，将不可以共享的状态从类里剔除出去。客户端不可以直接创建被共享的对象，而应当使用一个工厂对象负责创建
被共享的对象。享元模式大幅度的降低内存中对象的数量。 <br /><br />　　12、PROXY―跟MM在网上聊天，一开头总是“hi,你好”,“你从哪儿来呀？”“你多大了？”“身高多少呀？”这些话，真烦人，写个程序做为我的Proxy吧，凡是接收到这些话都设置好了自动的回答，接收到其他的话时再通知我回答，怎么样，酷吧。 <br /><br />　
　代理模式：代理模式给某一个对象提供一个代理对象，并由代理对象控制对源对象的引用。代理就是一个人或一个机构代表另一个人或者一个机构采取行动。某些
情况下，客户不想或者不能够直接引用一个对象，代理对象可以在客户和目标对象直接起到中介的作用。客户端分辨不出代理主题对象与真实主题对象。代理模式可
以并不知道真正的被代理对象，而仅仅持有一个被代理对象的接口，这时候代理对象不能够创建被代理对象，被代理对象必须有系统的其他角色代为创建并传入。
<br /><br /><strong>行为模式</strong></p>
		<p>　　13、CHAIN OF
RESPONSIBLEITY―晚上去上英语课，为了好开溜坐到了最后一排，哇，前面坐了好几个漂亮的MM哎，找张纸条，写上“Hi,可以做我的女朋友
吗？如果不愿意请向前传”，纸条就一个接一个的传上去了，糟糕，传到第一排的MM把纸条传给老师了，听说是个老处女呀，快跑! <br /><br />　　责任链模式：在责任链模式中，很多对象由每一个对象对其下家的引用而接 <br /><br />　
　起来形成一条链。请求在这个链上传递，直到链上的某一个对象决定处理此请求。客户并不知道链上的哪一个对象最终处理这个请求，系统可以在不影响客户端的
情况下动态的重新组织链和分配责任。处理者有两个选择：承担责任或者把责任推给下家。一个请求可以最终不被任何接收端对象所接受。 <br /><br />　
　14、COMMAND―俺有一个MM家里管得特别严，没法见面，只好借助于她弟弟在我们俩之间传送信息，她对我有什么指示，就写一张纸条让她弟弟带给
我。这不，她弟弟又传送过来一个COMMAND，为了感谢他，我请他吃了碗杂酱面，哪知道他说：“我同时给我姐姐三个男朋友送COMMAND，就数你最小
气，才请我吃面。”， <br /><br />　　命令模式：命令模式把一个请求或者操作封装到一个对象中。命令模式把发出命令的责任和执行命令的责任分割
开，委派给不同的对象。命令模式允许请求的一方和发送的一方独立开来，使得请求的一方不必知道接收请求的一方的接口，更不必知道请求是怎么被接收，以及操
作是否执行，何时被执行以及是怎么被执行的。系统支持命令的撤消。 <br /><br />　　15、INTERPRETER―俺有一个《泡MM真经》，上面有各种泡MM的攻略，比如说去吃西餐的步骤、去看电影的方法等等，跟MM约会时，只要做一个Interpreter，照着上面的脚本执行就可以了。 <br /><br />　
　解释器模式：给定一个语言后，解释器模式可以定义出其文法的一种表示，并同时提供一个解释器。客户端可以使用这个解释器来解释这个语言中的句子。解释器
模式将描述怎样在有了一个简单的文法后，使用模式设计解释这些语句。在解释器模式里面提到的语言是指任何解释器对象能够解释的任何组合。在解释器模式中需
要定义一个代表文法的命令类的等级结构，也就是一系列的组合规则。每一个命令对象都有一个解释方法，代表对命令对象的解释。命令对象的等级结构中的对象的
任何排列组合都是一个语言。 <br /><br />　　16、ITERATOR―我爱上了Mary，不顾一切的向她求婚。 <br /><br />　　Mary：“想要我跟你结婚，得答应我的条件” <br /><br />　　我：“什么条件我都答应，你说吧” <br /><br />　　Mary：“我看上了那个一克拉的钻石” <br /><br />　　我：“我买，我买，还有吗？” <br /><br />　　Mary：“我看上了湖边的那栋别墅” <br /><br />　　我：“我买，我买，还有吗？” <br /><br />　　Mary：“我看上那辆法拉利跑车” <br /><br />　　我脑袋嗡的一声，坐在椅子上，一咬牙：“我买，我买，还有吗？” <br /><br />　　…… <br /><br />　
　迭代子模式：迭代子模式可以顺序访问一个聚集中的元素而不必暴露聚集的内部表象。多个对象聚在一起形成的总体称之为聚集，聚集对象是能够包容一组对象的
容器对象。迭代子模式将迭代逻辑封装到一个独立的子对象中，从而与聚集本身隔开。迭代子模式简化了聚集的界面。每一个聚集对象都可以有一个或一个以上的迭
代子对象，每一个迭代子的迭代状态可以是彼此独立的。迭代算法可以独立于聚集角色变化。 <br /><br />　　17、MEDIATOR―四个MM打麻将，相互之间谁应该给谁多少钱算不清楚了，幸亏当时我在旁边，按照各自的筹码数算钱，赚了钱的从我这里拿，赔了钱的也付给我，一切就OK啦，俺得到了四个MM的电话。 <br /><br />　
　调停者模式：调停者模式包装了一系列对象相互作用的方式，使得这些对象不必相互明显作用。从而使他们可以松散偶合。当某些对象之间的作用发生改变时，不
会立即影响其他的一些对象之间的作用。保证这些作用可以彼此独立的变化。调停者模式将多对多的相互作用转化为一对多的相互作用。调停者模式将对象的行为和
协作抽象化，把对象在小尺度的行为上与其他对象的相互作用分开处理。 <br /><br />　　18、MEMENTO―同时跟几个MM聊天时，一定要记清楚刚才跟MM说了些什么话，不然MM发现了会不高兴的哦，幸亏我有个备忘录，刚才与哪个MM说了什么话我都拷贝一份放到备忘录里面保存，这样可以随时察看以前的记录啦。 <br /><br />　　备忘录模式：备忘录对象是一个用来存储另外一个对象内部状态的快照的对象。备忘录模式的用意是在不破坏封装的条件下，将一个对象的状态捉住，并外部化，存储起来，从而可以在将来合适的时候把这个对象还原到存储起来的状态。 <br /><br />　　19、OBSERVER―想知道咱们公司最新MM情报吗？加入公司的MM情报邮件组就行了，tom负责搜集情报，他发现的新情报不用一个一个通知我们，直接发布给邮件组，我们作为订阅者（观察者）就可以及时收到情报啦 <br /><br />　　观察者模式：观察者模式定义了一种一队多的依赖关系，让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时，会通知所有观察者对象，使他们能够自动更新自己。 <br /><br />　
　20、STATE―跟MM交往时，一定要注意她的状态哦，在不同的状态时她的行为会有不同，比如你约她今天晚上去看电影，对你没兴趣的MM就会说“有事
情啦”，对你不讨厌但还没喜欢上的MM就会说“好啊，不过可以带上我同事么？”，已经喜欢上你的MM就会说“几点钟？看完电影再去泡吧怎么样？”，当然你
看电影过程中表现良好的话，也可以把MM的状态从不讨厌不喜欢变成喜欢哦。 <br /><br />　　状态模式：状态模式允许一个对象在其内部状态改变的时
候改变行为。这个对象看上去象是改变了它的类一样。状态模式把所研究的对象的行为包装在不同的状态对象里，每一个状态对象都属于一个抽象状态类的一个子
类。状态模式的意图是让一个对象在其内部状态改变的时候，其行为也随之改变。状态模式需要对每一个系统可能取得的状态创立一个状态类的子类。当系统的状态
变化时，系统便改变所选的子类。 <br /><br />　　21、STRATEGY―跟不同类型的MM约会，要用不同的策略，有的请电影比较好，有的则去吃小吃效果不错，有的去海边浪漫最合适，单目的都是为了得到MM的芳心，我的追MM锦囊中有好多Strategy哦。 <br /><br />　
　策略模式：策略模式针对一组算法，将每一个算法封装到具有共同接口的独立的类中，从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情
况下发生变化。策略模式把行为和环境分开。环境类负责维持和查询行为类，各种算法在具体的策略类中提供。由于算法和环境独立开来，算法的增减，修改都不会
影响到环境和客户端。 <br /><br />　　22、TEMPLATE
METHOD――看过《如何说服女生上床》这部经典文章吗？女生从认识到上床的不变的步骤分为巧遇、打破僵局、展开追求、接吻、前戏、动手、爱抚、进去八
大步骤(Template method)，但每个步骤针对不同的情况，都有不一样的做法，这就要看你随机应变啦(具体实现)； <br /><br />　　
模板方法模式：模板方法模式准备一个抽象类，将部分逻辑以具体方法以及具体构造子的形式实现，然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类
可以以不同的方式实现这些抽象方法，从而对剩余的逻辑有不同的实现。先制定一个顶级逻辑框架，而将逻辑的细节留给具体的子类去实现。 <br /><br />　
　23、VISITOR―情人节到了，要给每个MM送一束鲜花和一张卡片，可是每个MM送的花都要针对她个人的特点，每张卡片也要根据个人的特点来挑，我
一个人哪搞得清楚，还是找花店老板和礼品店老板做一下Visitor，让花店老板根据MM的特点选一束花，让礼品店老板也根据每个人特点选一张卡，这样就
轻松多了； <br /><br />　　访问者模式：访问者模式的目的是封装一些施加于某种数据结构元素之上的操作。一旦这些操作需要修改的话，接受这个操作
的数据结构可以保持不变。访问者模式适用于数据结构相对未定的系统，它把数据结构和作用于结构上的操作之间的耦合解脱开，使得操作集合可以相对自由的演
化。访问者模式使得增加新的操作变的很容易，就是增加一个新的访问者类。访问者模式将有关的行为集中到一个访问者对象中，而不是分散到一个个的节点类中。
当使用访问者模式时，要将尽可能多的对象浏览逻辑放在访问者类中，而不是放到它的子类中。访问者模式可以跨过几个类的等级结构访问属于不同的等级结构的成
员类。</p>
<img src ="http://www.cnitblog.com/martin/aggbug/9173.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/martin/" target="_blank">Martin</a> 2006-04-14 11:41 <a href="http://www.cnitblog.com/martin/archive/2006/04/14/9173.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>