﻿<?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-庄周梦蝶，孰蝶是我，我是孰蝶？一梦至今，蝶我已难分-随笔分类-设计模式</title><link>http://www.blogjava.net/killme2008/category/19797.html</link><description /><language>zh-cn</language><lastBuildDate>Mon, 07 Apr 2008 16:31:33 GMT</lastBuildDate><pubDate>Mon, 07 Apr 2008 16:31:33 GMT</pubDate><ttl>60</ttl><item><title>模块的设计(书摘）</title><link>http://www.blogjava.net/killme2008/archive/2008/04/06/191022.html</link><dc:creator>dennis</dc:creator><author>dennis</author><pubDate>Sun, 06 Apr 2008 05:00:00 GMT</pubDate><guid>http://www.blogjava.net/killme2008/archive/2008/04/06/191022.html</guid><wfw:comment>http://www.blogjava.net/killme2008/comments/191022.html</wfw:comment><comments>http://www.blogjava.net/killme2008/archive/2008/04/06/191022.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/killme2008/comments/commentRss/191022.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/killme2008/services/trackbacks/191022.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; 模块化的价值毋庸置疑。<br />
&nbsp;&nbsp;&nbsp; 模块化代码的首要特质就是封装。封装良好的模块不会过多向外部披露自身的细节，不会直接调用其它模块的实现码，也不会胡乱共享全局数据。模块之间通过应用程序编程接口（API）——一组严密、定义良好的程序调用和数据结构来通信。这就是模块化原则的内容。API在模块间扮演双重角色。在实现层面，作为模块之间的滞塞点（choke point），阻止各自的内部细节被相邻模块知晓；在设计层面，正是API(而不是模块间的实现代码)真正定义了整个体系。<br />
&nbsp;&nbsp;&nbsp; 模块的最佳模块大小，逻辑行在200到400之间，物理行在400到800之间为最佳。模块太小，几乎所有的复杂度都集中在接口，同样不利于理解，也就是透明性的欠缺。<br />
&nbsp;&nbsp; 紧凑性就是一个特性能否装进人脑中的特性。理解紧凑性可以从它的&#8220;反面&#8221;来理解，紧凑性不等于&#8220;薄弱&#8221;，如果一个设计构建在易于理解且<strong>利于组合</strong>的抽象概念上，则这个系统能在具有非常强大、灵活的功能的同时保持紧凑。紧凑也不等同于&#8220;容易学习&#8221;：对于某些紧凑
设计而言，在掌握其精妙的内在基础概念模型之前，要理解这个设计相当困难；但一旦理解了这个概念模型，整个视角就会改变，紧凑的奥妙也就十分简单了。紧凑也不意味着&#8220;小巧&#8221;。即使一个设计良好的系统，对有经验的用户来说没什么特异之处、&#8220;一眼&#8221;就能看懂，但仍然可能包含很多部分。<br />
&nbsp;&nbsp;&nbsp; 评测一个API紧凑性的经验法则是：API的入口点通常在7个左右，或者按《代码大全2》的说法，7+2和7-2的范围内。<br />
&nbsp;&nbsp;&nbsp; <span style="font-family: 方正书宋简体;">如果两个或更多事物中的一个发生变化，不会影响其他事物，这些事物就是正交的。每一个动作只改变一件事，不会影响其他（没有其他副作用）。举个例子，比如读取配置文件，获得系统设置信息这个方法：<br />
</span>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000;">module&nbsp;Config<br />
&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">def</span><span style="color: #000000;">&nbsp;self.load_config<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;config_str</span><span style="color: #000000;">=</span><span style="color: #000000;">File.open(</span><span style="color: #800000;">"</span><span style="color: #800000;">r</span><span style="color: #800000;">"</span><span style="color: #000000;">,</span><span style="color: #800000;">"</span><span style="color: #800000;">config.xml</span><span style="color: #800000;">"</span><span style="color: #000000;">).read<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">#</span><span style="color: #008000;">解析配置文件，可能转化成XML&nbsp;Dom处理等</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://www.blogjava.net/Images/dot.gif"  alt="" /><img src="http://www.blogjava.net/Images/dot.gif"  alt="" /><br />
&nbsp;&nbsp;&nbsp;end<br />
end</span></div>
这个方法想当然地认为配置文件存储于磁盘文件中，然而配置文件完全是有可能通过网络获取的，也就是说文件句柄未必来源于磁盘文件，这个方法承担了两个职责：获取配置数据和解析配置数据。重构一下，以提高正交性：<br />
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000;">module&nbsp;Config<br />
&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">def</span><span style="color: #000000;">&nbsp;self.load_config(io)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;config_str</span><span style="color: #000000;">=</span><span style="color: #000000;">io.read<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;parse_config(config_str)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;end<br />
&nbsp;&nbsp;&nbsp;private<br />
&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">def</span><span style="color: #000000;">&nbsp;self.parse_config(config_str)<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">#</span><span style="color: #008000;">解析<img src="http://www.blogjava.net/Images/dot.gif"  alt="" /></span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;end&nbsp;<br />
end</span></div>
&nbsp;&nbsp;&nbsp; 重构技术中的很多坏味道，特别是重复代码，是违反正交性的明显例子，&#8220;重构的原则性目标就是提高正交性&#8221;。<br />
&nbsp;&nbsp;&nbsp; DRY（Don't Repeat Yourself)原则，意思是说：任何一个知识点在系统内都应当有一个唯一、明确、权威的表述。这个原则的另一种表述就是所谓SPOT原则（Single Point Of Truth）——真理的单点性。<br />
&nbsp;&nbsp;&nbsp; 提高设计的紧凑性，有一个精妙但强大的方法，就是围绕&#8220;解决一个定义明确的问题&#8221;的强核心算法组织设计，避免臆断和捏造。<br />
<br /><img src ="http://www.blogjava.net/killme2008/aggbug/191022.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killme2008/" target="_blank">dennis</a> 2008-04-06 13:00 <a href="http://www.blogjava.net/killme2008/archive/2008/04/06/191022.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>singleton迷恋</title><link>http://www.blogjava.net/killme2008/archive/2008/02/23/181596.html</link><dc:creator>dennis</dc:creator><author>dennis</author><pubDate>Sat, 23 Feb 2008 07:34:00 GMT</pubDate><guid>http://www.blogjava.net/killme2008/archive/2008/02/23/181596.html</guid><wfw:comment>http://www.blogjava.net/killme2008/comments/181596.html</wfw:comment><comments>http://www.blogjava.net/killme2008/archive/2008/02/23/181596.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.blogjava.net/killme2008/comments/commentRss/181596.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/killme2008/services/trackbacks/181596.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; 自从知道singleton模式这把锤子是什么样的之后，我就把很多小疙瘩也当成了钉子，时常想象偶顶着模式的光环挥舞着&#8220;万能&#8221;锤子在代码丛林中学习兰博搏斗的光辉形象~~~~。昨天读《重构与模式》的inline singleton一节，一句话点醒梦中人，singleton模式是&#8220;保证一个类仅有一个实例，并提供一个访问它的全局访问点&#8221;，原来——singleton也是全局变量啊。马教主深刻地教育过我们：全局变量在被证明无害之前都是有害。偶大汗淋漓。看看迷恋singleton的几种现象：<br />
1、仅仅在一个地方调用到了某个singleton实例，并且对这个实例的处理代码也集中在这么一两个地方，这样的情况下你为什么要singleton？这里需要一个个全局访问点吗？我看你是为了singleton而singleton。<br />
<br />
2、我为了性能优化啊！singleton只有一个实例，减小了创建开销。oh,我终于找到一个用singleton的充分理由了——性能。慢着，跟我读高大师的名言：&#8220;<span class="5K"><span style="font-family: 楷体_GB2312;">不成熟的优化是万恶之源&#8221;</span></span>。你怎么知道singleton对象的重复创建是明显影响了性能？现代jvm对&#8220;短命&#8221;对象的创建代价已经非常低了。不成熟的优化不仅可能是无效的，而且也给以后重构工作带来了困难。除非有明显数据证明（分析工具而来）某个对象的重复创建是对性能影响极大，否则所谓性能优化不能成为采用singleton模式的理由。<br />
<br />
3、有时候我们需要在系统的不同层次间传递一些共享信息，如果不采用singleton对象来提供这些共享信息，就得在调用的方法中重复地传递这些参数，这是个应用singleton模式的场景。但是，如果这些共享信息是可被修改的，或者说singleton对象不是无状态的，如果还采用singleton模式，那么你就不得不在调用的方法中从single对象取出旧信息和存入新信息，这样的重复代码将遍布的到处都是，不仅仅引入了同步访问的需要，而且出错的风险大大提高。这种情况下你还将这些信息作为方法参数传递而不是采用singleton可能更为清晰和健壮。<br />
<br />
&nbsp;&nbsp;&nbsp; singleton不仅仅是&#8220;保证一个类仅有一个实例&#8221;（这仅仅是描述），更重要的是它是用来&#8220;提供全局访问点&#8221;的（这才是它的功能），不要再迷恋这把锤子，好好利用这把锤子。<br />
<br />
题外话：脚本语言似乎更容易滥用全局变量，javascript里可以模拟命名空间，Ruby也可以模拟类似的机制。最近写的一个比较大一点的Ruby脚本，用了几个全局变量（都是数组）用于保存状态数据，一开始没有意识到这一点，导致对全局变量的访问散落在好几个脚本文件里，RDT下看起来红通通的一片极其不爽。那么就重构吧——封装数组重构，将对这些全局数组的访问和修改操作统一到一个模块，调用全局变量的地方都引用这个模块，通过模块去操作全局变量，代码看起来清爽多了。<br />
<br />
<br /><img src ="http://www.blogjava.net/killme2008/aggbug/181596.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killme2008/" target="_blank">dennis</a> 2008-02-23 15:34 <a href="http://www.blogjava.net/killme2008/archive/2008/02/23/181596.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>The Future Of the Software Development</title><link>http://www.blogjava.net/killme2008/archive/2007/10/25/155788.html</link><dc:creator>dennis</dc:creator><author>dennis</author><pubDate>Thu, 25 Oct 2007 02:11:00 GMT</pubDate><guid>http://www.blogjava.net/killme2008/archive/2007/10/25/155788.html</guid><wfw:comment>http://www.blogjava.net/killme2008/comments/155788.html</wfw:comment><comments>http://www.blogjava.net/killme2008/archive/2007/10/25/155788.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.blogjava.net/killme2008/comments/commentRss/155788.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/killme2008/services/trackbacks/155788.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 在未来，我们可能需要的是一个个高效精干的小型团队，团队成员技艺高超，富于激情，易于沟通。&nbsp;&nbsp;<a href='http://www.blogjava.net/killme2008/archive/2007/10/25/155788.html'>阅读全文</a><img src ="http://www.blogjava.net/killme2008/aggbug/155788.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killme2008/" target="_blank">dennis</a> 2007-10-25 10:11 <a href="http://www.blogjava.net/killme2008/archive/2007/10/25/155788.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>谈NullObject模式</title><link>http://www.blogjava.net/killme2008/archive/2007/07/31/133628.html</link><dc:creator>dennis</dc:creator><author>dennis</author><pubDate>Tue, 31 Jul 2007 09:48:00 GMT</pubDate><guid>http://www.blogjava.net/killme2008/archive/2007/07/31/133628.html</guid><wfw:comment>http://www.blogjava.net/killme2008/comments/133628.html</wfw:comment><comments>http://www.blogjava.net/killme2008/archive/2007/07/31/133628.html#Feedback</comments><slash:comments>7</slash:comments><wfw:commentRss>http://www.blogjava.net/killme2008/comments/commentRss/133628.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/killme2008/services/trackbacks/133628.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; 知道这个模式还是通过《重构》，这个模式的出现还是了为了解决代码重复的坏味道。在项目中很经常见到类似下面这样的代码：<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">if</span><span style="color: #000000;">(prj.getProjectId</span><span style="color: #000000;">==</span><span style="color: #0000ff;">null</span><span style="color: #000000;">)<br>&nbsp;&nbsp;&nbsp;&nbsp;plan.setCost(</span><span style="color: #000000;">0.0</span><span style="color: #000000;">);<br></span><span style="color: #0000ff;">else</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;plan.setCost(prj.getCost());</span></div>
<br>&nbsp;&nbsp; 我们在很多地方有类似的检查对象是否为null，如果为null，需要一个默认值等等这样的场景。显然，代码重复是坏味道，怎么消除这个坏味道呢？答案就是使用NullObject替代之，Null Object继承原对象。<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;NullProject&nbsp;</span><span style="color: #0000ff;">extends</span><span style="color: #000000;">&nbsp;Project{<br>&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">boolean</span><span style="color: #000000;">&nbsp;isNull(){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">true</span><span style="color: #000000;">;<br>&nbsp;&nbsp;&nbsp;}<br>}<br></span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;Project{<br>&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">double</span><span style="color: #000000;">&nbsp;cost;<br>&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;String&nbsp;projectId;<br>&nbsp;&nbsp;&nbsp;<img src="http://www.blogjava.net/Images/dot.gif">.<br>&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">boolean</span><span style="color: #000000;">&nbsp;isNull(){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">false</span><span style="color: #000000;">;<br>&nbsp;&nbsp;&nbsp;}<br>}</span></div>
<br>那么，原来的代码可以改写为：<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">if</span><span style="color: #000000;">(prj.isNull())<br>&nbsp;&nbsp;&nbsp;&nbsp;plan.setCost(</span><span style="color: #000000;">0.0</span><span style="color: #000000;">);<br></span><span style="color: #0000ff;">else</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;plan.setCost(prj.getCost());</span></div>
<br>&nbsp;&nbsp;&nbsp; 如果Null Object的引入仅仅是带来这个好处，似乎没有理由让我们多敲这么多键盘。问题的关键是类似上面这样的判断也许出现在很多处，那么有价值的技巧出现了，我们在NullObject覆写getCost，提供缺省值：<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;NullProject&nbsp;</span><span style="color: #0000ff;">extends</span><span style="color: #000000;">&nbsp;Project{<br>&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">boolean</span><span style="color: #000000;">&nbsp;isNull(){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">true</span><span style="color: #000000;">;<br>&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">double</span><span style="color: #000000;">&nbsp;getCost(){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">0.0</span><span style="color: #000000;">;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;}<br>}</span></div>
&nbsp;&nbsp;&nbsp; 因此，检查对象是否为null的代码可以去掉if...else了：<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #000000;">plan.setCost(prj.getCost());</span></div>
<br>&nbsp;&nbsp;&nbsp; 请注意，只有那些大多数客户端代码都要求null object做出相同响应时，这样的行为才有意义。比如我们这里当工程id为null，很多地方要求费用就默认为0.0。
特殊的行为我们仍然使用isNull进行判断。<br>&nbsp;&nbsp;&nbsp; 当然，另外在需要返回NullObject的地方，你应该创建一个null object以替代一般的对象，我们可以建立一个工厂方法：<br><br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;Project{<br>&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">double</span><span style="color: #000000;">&nbsp;cost;<br>&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;String&nbsp;projectId;<br>&nbsp;&nbsp;&nbsp;<img src="http://www.blogjava.net/Images/dot.gif"><img src="http://www.blogjava.net/Images/dot.gif">.<br>&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">boolean</span><span style="color: #000000;">&nbsp;isNull(){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">false</span><span style="color: #000000;">;<br>&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;Project&nbsp;createNullProject(){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;NullProject();<br>&nbsp;&nbsp;&nbsp;}<br>}</span></div>
<br>&nbsp;&nbsp; Null Object模式带来的好处：减少了检查对象是否为null的代码重复，提高了代码的可读性，通常这些Null Object也可以为单元测试带来简便。<br><br><img src ="http://www.blogjava.net/killme2008/aggbug/133628.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killme2008/" target="_blank">dennis</a> 2007-07-31 17:48 <a href="http://www.blogjava.net/killme2008/archive/2007/07/31/133628.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JUnit源码分析(四)——从Decorator模式说起</title><link>http://www.blogjava.net/killme2008/archive/2007/04/06/108894.html</link><dc:creator>dennis</dc:creator><author>dennis</author><pubDate>Fri, 06 Apr 2007 04:39:00 GMT</pubDate><guid>http://www.blogjava.net/killme2008/archive/2007/04/06/108894.html</guid><wfw:comment>http://www.blogjava.net/killme2008/comments/108894.html</wfw:comment><comments>http://www.blogjava.net/killme2008/archive/2007/04/06/108894.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/killme2008/comments/commentRss/108894.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/killme2008/services/trackbacks/108894.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; 其实我这系列小文，名为源码分析，其实是自己读《设计模式》的读书笔记。Decorator模式在java的IO库中得到应用，java的IO库看起来复杂，其实理解了Decorator模式再回头看可以很好理解并使用。<br>&nbsp;&nbsp;&nbsp; Decorator模式，也就是装饰器模式，是对象结构型模式之一。<br><br>1.意图：动态地给一个对象添加一些额外的职责。给对象添加功能，我们首先想到的是继承，但是如果每增一个功能都需要继承，类的继承体系将无可避免地变的庞大和难以理解。面向对象设计的原则：优先使用组合，而非继承，继承的层次深度最好不过三。<br><br>2.适用场景：<br>1）在不影响其他对象的情况下，以动态、透明的方式给单个对象添加额外的责任<br>2）处理可以撤销的职责<br>3）为了避免类的数目爆炸，或者不能采用生成子类的方法进行扩展时<br><br>3.UML图和协作：<br><br><img src="http://www.blogjava.net/images/blogjava_net/killme2008/ShowImg.jpg" border="0"><br><br>Component——定义一个对象接口，可以给这些对象动态地添加职责<br><br>ConcreteComponent——定义一个对象，可以给这个对象添加职责<br><br>Decorator——维持一个指向Component的引用，并定义一个与Component一致的接口，作为装饰类的父类<br><br>ConcreteDecorator——具体装饰类<br><br>4.效果：<br>1）与静态继承相比，Decorator可以动态添加职责，更为灵活<br>2）避免产生复杂的类，通过动态添加职责，而不是一次性提供一个万能的接口<br>3）缺点是将产生比较多的小对象，对学习上有难度，显然，java.io就是这个问题<br><br>我们以一个例子来实现Decorator模式，假设这样一个场景：在某个应用中需要打印票据，我们写了一个PrintTicket接口，然后提供一个实现类（DefaultPrintTicket）实现打印的功能：<br><br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">package</span><span style="color: #000000;">&nbsp;com.rubyeye.design_pattern.decorator;<br></span><span style="color: #008000;">//</span><span style="color: #008000;">抽象component接口</span><span style="color: #008000;"><br></span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">interface</span><span style="color: #000000;">&nbsp;PrintTicket&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;print();<br>}<br><br><br></span><span style="color: #008000;">//</span><span style="color: #008000;">默认实现类，打印票据</span><span style="color: #008000;"><br></span><span style="color: #0000ff;">package</span><span style="color: #000000;">&nbsp;com.rubyeye.design_pattern.decorator;<br><br></span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;DefaultPrintTicket&nbsp;</span><span style="color: #0000ff;">implements</span><span style="color: #000000;">&nbsp;PrintTicket&nbsp;{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;print()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="color: #000000;">"</span><span style="color: #000000;">ticket&nbsp;body</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>}<br></span></div>
<br>OK,我们的功能已经实现，我们还体现了针对接口编程的原则，替换一个新的打印方式很灵活，但是客户开始提需求了——人生无法避免的三件事：交税、死亡和需求变更。客户要求打印页眉，你首先想到的是继承：<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">package</span><span style="color: #000000;">&nbsp;com.rubyeye.design_pattern.decorator;<br><br></span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;AnotherPrintTicket&nbsp;</span><span style="color: #0000ff;">implements</span><span style="color: #000000;">&nbsp;PrintTicket&nbsp;{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;print()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="color: #000000;">"</span><span style="color: #000000;">ticket&nbsp;header</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="color: #000000;">"</span><span style="color: #000000;">ticket&nbsp;body</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>}<br></span></div>
<br>请注意，我们这里只是简单的示例，在实际项目中也许意味着添加一大段代码，并且需要修改打印票据本体的功能。需求接踵而至，客户要求添加打印页码，要求增加打印花纹，要求可以联打......你的类越来越庞大，直到你看见这个类都想吐的地步！-_-。让我们看看另一个方案，使用Decorator模式来动态地给打印增加一些功能，首先是实现一个Decorator，它需要保持一个到PrintTicket接口的引用：<br><br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">package</span><span style="color: #000000;">&nbsp;com.rubyeye.design_pattern.decorator;<br><br></span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;PrintTicketDecorator&nbsp;</span><span style="color: #0000ff;">implements</span><span style="color: #000000;">&nbsp;PrintTicket&nbsp;{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">protected</span><span style="color: #000000;">&nbsp;PrintTicket&nbsp;printTicket;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;PrintTicketDecorator(PrintTicket&nbsp;printTicket)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.printTicket&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;printTicket;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">默认调用PrintTicket的print</span><span style="color: #008000;"><br></span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;print()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printTicket.print();<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>}<br></span></div>
<br>然后，我们实现两个具体的装饰类——打印页眉和页脚：<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">package</span><span style="color: #000000;">&nbsp;com.rubyeye.design_pattern.decorator;<br><br></span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;HeaderPrintTicket&nbsp;</span><span style="color: #0000ff;">extends</span><span style="color: #000000;">&nbsp;PrintTicketDecorator&nbsp;{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;HeaderPrintTicket(PrintTicket&nbsp;printTicket){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">super</span><span style="color: #000000;">(printTicket);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;print()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="color: #000000;">"</span><span style="color: #000000;">ticket&nbsp;header</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">super</span><span style="color: #000000;">.print();<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></span></div>
<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">package</span><span style="color: #000000;">&nbsp;com.rubyeye.design_pattern.decorator;<br><br></span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;FooterPrintTicket&nbsp;</span><span style="color: #0000ff;">extends</span><span style="color: #000000;">&nbsp;PrintTicketDecorator&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;FooterPrintTicket(PrintTicket&nbsp;printTicket)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">super</span><span style="color: #000000;">(printTicket);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;print()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">super</span><span style="color: #000000;">.print();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="color: #000000;">"</span><span style="color: #000000;">ticket&nbsp;footer</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></span></div>
<br>&nbsp;&nbsp;&nbsp; 使用起来也很容易：<br>&nbsp;&nbsp;&nbsp;
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">package</span><span style="color: #000000;">&nbsp;com.rubyeye.design_pattern.decorator;<br><br></span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;DecoratorTest&nbsp;{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080;">@param</span><span style="color: #008000;">&nbsp;args<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;main(String[]&nbsp;args)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;PrintTicket&nbsp;print</span><span style="color: #000000;">=</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;HeaderPrintTicket(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;FooterPrintTicket(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;DefaultPrintTicket()));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print.print();<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>}<br></span></div>
<br>输出：<br>ticket header<br>ticket body<br>ticket footer<br><br>&nbsp;&nbsp;&nbsp; 了解了Decorator模式，我们联系了下JUnit里面的应用。作为一个测试框架，应该方便地支持二次开发，也许用户开发自己的TestCase，添加自定义的功能，比如执行重复测试、多线程测试等等。动态添加职责，而又不想使用静态继承，这正是Decorator使用的地方。在junit.extensions包中有一个TestDecorator，正是所有装饰类的父类，也是作为二次开发的基础，它实现了Test接口，而Test接口就是我们定义的抽象接口：<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;TestDecorator&nbsp;</span><span style="color: #0000ff;">implements</span><span style="color: #000000;">&nbsp;&nbsp;Test&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">保有一个指向Test的引用</span><span style="color: #008000;"><br></span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">protected</span><span style="color: #000000;">&nbsp;Test&nbsp;fTest;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;TestDecorator(Test&nbsp;test)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fTest</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;test;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://www.blogjava.net/Images/dot.gif"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;basicRun(TestResult&nbsp;result) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp; fTest.run(result);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;run(TestResult&nbsp;result)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; basicRun(result);<br>&nbsp;&nbsp; &nbsp; &nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://www.blogjava.net/Images/dot.gif"><br>}</span></div>
<br>Junit已经提供了两个装饰类：junit.extensions.ActiveTest用于处理多线程，junit.extensions.RepeatedTest用于执行重复测试，看看RepeatedTest是怎么实现的：<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;RepeatedTest&nbsp;</span><span style="color: #0000ff;">extends</span><span style="color: #000000;">&nbsp;&nbsp;TestDecorator&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">重复次数</span><span style="color: #008000;"><br></span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;fTimesRepeat;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;RepeatedTest(Test&nbsp;test,&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;repeat)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">super</span><span style="color: #000000;">(test);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fTimesRepeat</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;repeat;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;run(TestResult&nbsp;result)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">重复执行</span><span style="color: #008000;"><br></span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">for</span><span style="color: #000000;">&nbsp;(</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;i</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">;&nbsp;i&nbsp;</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">&nbsp;fTimesRepeat;&nbsp;i</span><span style="color: #000000;">++</span><span style="color: #000000;">)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(result.shouldStop())<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">break</span><span style="color: #000000;">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">super</span><span style="color: #000000;">.run(result);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://www.blogjava.net/Images/dot.gif"><img src="http://www.blogjava.net/Images/dot.gif"><br>}</span></div>
<br>&nbsp;&nbsp;&nbsp; <span style="color: #000000;">RepeatedTest继承</span><span style="color: #000000;">TestDecorator ，覆写</span><span style="color: #000000;">run（TestReult result）方法，重复执行，super.run(result)将调用传入的TestCase的run(TestResult result)方法，这已经在</span><span style="color: #000000;"></span><span style="color: #000000;">TestDecorator默认实现。看看使用方式，使用装饰模式的好处不言而喻。<br><br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #000000;">TestSuite&nbsp;suite&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;TestSuite();<br>suite.addTest(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;TestSetup(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;RepeatedTest(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Testmath(</span><span style="color: #000000;">"</span><span style="color: #000000;">testAdd</span><span style="color: #000000;">"</span><span style="color: #000000;">),</span><span style="color: #000000;">12</span><span style="color: #000000;">)));<br><br></span></div>
</span><span style="color: #0000ff;"></span> <img src ="http://www.blogjava.net/killme2008/aggbug/108894.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killme2008/" target="_blank">dennis</a> 2007-04-06 12:39 <a href="http://www.blogjava.net/killme2008/archive/2007/04/06/108894.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JUnit源码分析 （三）——Template Method模式</title><link>http://www.blogjava.net/killme2008/archive/2007/04/06/108860.html</link><dc:creator>dennis</dc:creator><author>dennis</author><pubDate>Fri, 06 Apr 2007 01:54:00 GMT</pubDate><guid>http://www.blogjava.net/killme2008/archive/2007/04/06/108860.html</guid><wfw:comment>http://www.blogjava.net/killme2008/comments/108860.html</wfw:comment><comments>http://www.blogjava.net/killme2008/archive/2007/04/06/108860.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/killme2008/comments/commentRss/108860.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/killme2008/services/trackbacks/108860.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; 在JUnit执行测试时，我们经常需要初始化一些环境供测试代码使用，比如数据库连接、mock对象等等，这些初始化代码应当在每一个测试之前执行并在测试方法运行后清理。在JUnit里面就是相应的setUp和tearDown方法。如果没有这两个方法，那么我们要在每个测试方法的代码内写上一大堆重复的初始化和清理代码，这是多么愚蠢的做法。那么JUnit是怎么让setUp和tearDown在测试执行前后被调用的呢？<br>&nbsp;&nbsp;&nbsp; 如果你查看下TestCase方法，你会发现TestCase和TestSuite的run()方法都是将执行测试的任务委托给了TestResult，由TestResult去执行测试代码并收集测试过程中的信息（这里用到了Collecting Parameter模式）。<br>&nbsp;&nbsp;&nbsp;
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">&nbsp;&nbsp;&nbsp; public</span><span style="color: #000000;">&nbsp;TestResult&nbsp;run()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TestResult&nbsp;result</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;createResult();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;run(result);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;result;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;Runs&nbsp;the&nbsp;test&nbsp;case&nbsp;and&nbsp;collects&nbsp;the&nbsp;results&nbsp;in&nbsp;TestResult.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;This&nbsp;is&nbsp;the&nbsp;template&nbsp;method&nbsp;that&nbsp;defines&nbsp;the&nbsp;control&nbsp;flow<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;for&nbsp;running&nbsp;a&nbsp;test&nbsp;case.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;run(TestResult&nbsp;result)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;result.run(</span><span style="color: #0000ff;">this</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;}</span></div>
&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; 我们直接找到TestResult，看看它的run方法：<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #008000;">/**</span><span style="color: #008000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;Runs&nbsp;a&nbsp;TestCase.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">protected</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;run(</span><span style="color: #0000ff;">final</span><span style="color: #000000;">&nbsp;TestCase&nbsp;test)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;startTest(test);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Protectable&nbsp;p&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Protectable()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;protect()&nbsp;</span><span style="color: #0000ff;">throws</span><span style="color: #000000;">&nbsp;Throwable&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;test.runBare();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;};<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;runProtected(test,&nbsp;p);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;endTest(test);<br>&nbsp;&nbsp;&nbsp;&nbsp;}</span></div>
<br>&nbsp;&nbsp;&nbsp; 这里实例化了一个内部类，内部类实现了<span style="color: #000000;">Protectable接口的 </span><span style="color: #000000;">protect()方法，并执行传入的TestCase的runBare()方法，显然，真正的测试代码在TestCase的runBare()方法中，让我们来看下：<br></span><span style="color: #000000;"><br><br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">将被子类实现</span><span style="color: #008000;"><br></span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">protected</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;setUp()&nbsp;</span><span style="color: #0000ff;">throws</span><span style="color: #000000;">&nbsp;Throwable&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">同上，将被具体的TestCase实现</span><span style="color: #008000;"><br></span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">protected</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;tearDown()&nbsp;</span><span style="color: #0000ff;">throws</span><span style="color: #000000;">&nbsp;Throwable&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br></span><span style="color: #008000;">&nbsp;&nbsp;&nbsp;&nbsp; /**</span><span style="color: #008000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;模板方法<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;Runs&nbsp;the&nbsp;bare&nbsp;test&nbsp;sequence.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080;">@exception</span><span style="color: #008000;">&nbsp;Throwable&nbsp;if&nbsp;any&nbsp;exception&nbsp;is&nbsp;thrown<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;runBare()&nbsp;</span><span style="color: #0000ff;">throws</span><span style="color: #000000;">&nbsp;Throwable&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setUp();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">try</span><span style="color: #000000;">&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;runTest();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">finally</span><span style="color: #000000;">&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tearDown();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br></span></div>
</span><span style="color: #000000;">真相水落石出，对于每一个测试方法，都遵循这样的模板：setUp-&gt;执行测试 runTest()-&gt;tearDown。这正是模板方式模式的一个应用例子。什么是template method模式呢？<br><br>Template Method模式<br><br>类行为模式的一种<br>1.意图：定义一个操作中的算法的骨架，而将一些延迟步骤到子类中。Template Method使得子类可以不改变一个算法的结构即可重定义该算法的某些步骤。<br>2.适用场景：<br>1）一次性实现算法的不变部分（基本骨架），将可变的行为留给子类来完成<br>2）子类中的公共部分（比如JUnit中的初始化和清理）被抽取到一个公共父类中以避免代码重复。<br>3）控制了子类的扩展，这里其实也有类似回调函数的性质，具体步骤先在骨架中注册，在具体执行时被回调。<br><br>3.UML图和结构<br>&nbsp;&nbsp;&nbsp; <img  src="http://www.blogjava.net/images/blogjava_net/killme2008/template.jpg" border="0"><br>&nbsp; 抽象父类定义了算法的基本骨架（模板方法），而不同的子类实现具体的算法步骤，客户端由此可以与算法的更改隔离。<br><br>4.效果：<br>1）模板方法是代码复用的基本技术，在类库中经常使用，可以减少大量的代码重复<br>2）通过隔离算法的不变和可变部分，增加了系统的灵活性，扩展算法的某些步骤将变的很容易。<br><br>&nbsp;&nbsp;&nbsp; 了解了Template Method模式之后，让我们回到JUnit的源码，看看runTest()方法，这里主要应用的是java的反射技术，对于学习反射技术的有参考价值：<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">protected</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;runTest()&nbsp;</span><span style="color: #0000ff;">throws</span><span style="color: #000000;">&nbsp;Throwable&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Method&nbsp;runMethod</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">try</span><span style="color: #000000;">&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;runMethod</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;getClass().getDeclaredMethod(fName,&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Class[</span><span style="color: #000000;">0</span><span style="color: #000000;">]);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000ff;">catch</span><span style="color: #000000;">&nbsp;(NoSuchMethodException&nbsp;e)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fail(</span><span style="color: #000000;">"</span><span style="color: #000000;">Method&nbsp;\</span><span style="color: #000000;">""</span><span style="color: #000000;">+fName+</span><span style="color: #000000;">"</span><span style="color: #000000;">\</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;not&nbsp;found</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(runMethod&nbsp;</span><span style="color: #000000;">!=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">&amp;&amp;</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">!</span><span style="color: #000000;">Modifier.isPublic(runMethod.getModifiers()))&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fail(</span><span style="color: #000000;">"</span><span style="color: #000000;">Method&nbsp;\</span><span style="color: #000000;">""</span><span style="color: #000000;">+fName+</span><span style="color: #000000;">"</span><span style="color: #000000;">\</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;should&nbsp;be&nbsp;public</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">try</span><span style="color: #000000;">&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;runMethod.invoke(</span><span style="color: #0000ff;">this</span><span style="color: #000000;">,&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Class[</span><span style="color: #000000;">0</span><span style="color: #000000;">]);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">catch</span><span style="color: #000000;">&nbsp;(InvocationTargetException&nbsp;e)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.fillInStackTrace();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">throw</span><span style="color: #000000;">&nbsp;e.getTargetException();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">catch</span><span style="color: #000000;">&nbsp;(IllegalAccessException&nbsp;e)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.fillInStackTrace();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">throw</span><span style="color: #000000;">&nbsp;e;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}</span></div>
<br></span>&nbsp;&nbsp;&nbsp; <br><img src ="http://www.blogjava.net/killme2008/aggbug/108860.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killme2008/" target="_blank">dennis</a> 2007-04-06 09:54 <a href="http://www.blogjava.net/killme2008/archive/2007/04/06/108860.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JUnit源码分析（二）——观察者模式</title><link>http://www.blogjava.net/killme2008/archive/2007/04/05/108743.html</link><dc:creator>dennis</dc:creator><author>dennis</author><pubDate>Thu, 05 Apr 2007 09:19:00 GMT</pubDate><guid>http://www.blogjava.net/killme2008/archive/2007/04/05/108743.html</guid><wfw:comment>http://www.blogjava.net/killme2008/comments/108743.html</wfw:comment><comments>http://www.blogjava.net/killme2008/archive/2007/04/05/108743.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/killme2008/comments/commentRss/108743.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/killme2008/services/trackbacks/108743.html</trackback:ping><description><![CDATA[<br>&nbsp;&nbsp;&nbsp; 我们知道JUnit支持不同的使用方式：swt、swing的UI方式，甚至控制台方式，那么对于这些不同的UI我们如何提供统一的接口供它们获取测试过程的信息（比如出现的异常信息，测试成功，测试失败的代码行数等等）？我们试想一下这个场景，当一个error或者exception产生的时候，测试能够马上通知这些UI客户端：发生错误了，发生了什么错误，错误是什么等等。显而易见，这是一个订阅-发布机制应用的场景，应当使用观察者模式。那么什么是观察者模式呢？<br><br><span style="font-weight: bold;">观察者模式(Observer)<br><br></span>Observer是对象行为型模式之一<br><br>1.意图：定义对象间的一种一对多的依赖关系，当一个对象的状态发现改变时，所有依赖于它的对象都得到通知并被自动更新<br><br>2.适用场景：<br>1）当一个抽象模型有两个方面，其中一个方面依赖于另一个方面，通过观察者模式将这两者封装在不同的独立对象当中，以使它们可以独立的变化和复用<br>2）当一个对象改变时，需要同时改变其他对象，并且不知道其他对象的具体数目<br>3）当一个对象需要引用其他对象，但是你又不想让这个对象与其他对象产生紧耦合的时候<br><br>3.UML图：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <img src="http://www.blogjava.net/images/blogjava_net/killme2008/observer.jpg" border="0"><br>&nbsp;&nbsp; Subject及其子类维护一个观察者列表，当需要通知所有的Observer对象时调用Nitify方法遍历Observer集合，并调用它们的update方法更新。而具体的观察者实现Observer接口（或者抽象类），提供具体的更新行为。其实看这张图，与Bridge有几分相似，当然两者的意图和适用场景不同。<br><br>4.效果：<br>1）目标和观察者的抽象耦合,目标仅仅与抽象层次的简单接口Observer松耦合，而没有与具体的观察者紧耦合<br>2）支持广播通信<br>3）缺点是可能导致意外的更新，因为一个观察者并不知道其他观察者，它的更新行为也许将导致一连串不可预测的更新的行为<br><br>5.对于观察者实现需要注意的几个问题：<br>1）谁来触发更新？最好是由Subject通知观察者更新，而不是客户，因为客户可能忘记调用Notify<br>2)可以通过显式传参来指定感兴趣的更新<br>3）在发出通知前，确保Subject对象状态的一致性，也就是Notify操作应该在最后被调用<br>4）当Subject和Observer的依赖关系比较复杂的时候，可以通过一个更新管理器来管理它们之间的关系，这是与中介者模式的结合应用。<br><br><br>&nbsp;&nbsp;&nbsp; 讨论完观察者模式，那我们来看JUnit是怎么实现这个模式的。在junit.framework包中我们看到了一个Observer接口——TestListener，看看它的代码：<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">package</span><span style="color: #000000;">&nbsp;junit.framework;<br><br></span><span style="color: #008000;">/**</span><span style="color: #008000;"><br>&nbsp;*&nbsp;A&nbsp;Listener&nbsp;for&nbsp;test&nbsp;progress<br>&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br></span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">interface</span><span style="color: #000000;">&nbsp;TestListener&nbsp;{<br>&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;An&nbsp;error&nbsp;occurred.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;addError(Test&nbsp;test,&nbsp;Throwable&nbsp;t);<br>&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;A&nbsp;failure&nbsp;occurred.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;addFailure(Test&nbsp;test,&nbsp;Throwable&nbsp;t);&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;A&nbsp;test&nbsp;ended.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;endTest(Test&nbsp;test);&nbsp;<br>&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;A&nbsp;test&nbsp;started.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;startTest(Test&nbsp;test);<br>}</span></div>
<br>&nbsp;&nbsp;&nbsp; 接口清晰易懂，就是一系列将测试过程的信息传递给观察者的操作。具体的子类将接受这些信息，并按照它们的方式显示给用户。<br>&nbsp;&nbsp;&nbsp;&nbsp; 比如，我们看看swing的UI中的TestRunner，它将这些信息显示在一个swing写的UI界面上：<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;startTest(Test&nbsp;test)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;showInfo(</span><span style="color: #000000;">"</span><span style="color: #000000;">Running:&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">+</span><span style="color: #000000;">test);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br></span><span style="color: #0000ff;">&nbsp;&nbsp;&nbsp; public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;addError(Test&nbsp;test,&nbsp;Throwable&nbsp;t)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fNumberOfErrors.setText(Integer.toString(fTestResult.errorCount()));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;appendFailure(</span><span style="color: #000000;">"</span><span style="color: #000000;">Error</span><span style="color: #000000;">"</span><span style="color: #000000;">,&nbsp;test,&nbsp;t);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;addFailure(Test&nbsp;test,&nbsp;Throwable&nbsp;t)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fNumberOfFailures.setText(Integer.toString(fTestResult.failureCount()));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;appendFailure(</span><span style="color: #000000;">"</span><span style="color: #000000;">Failure</span><span style="color: #000000;">"</span><span style="color: #000000;">,&nbsp;test,&nbsp;t);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br></span><span style="color: #0000ff;">&nbsp;&nbsp;&nbsp; public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;endTest(Test&nbsp;test)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setLabelValue(fNumberOfRuns,&nbsp;fTestResult.runCount());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fProgressIndicator.step(fTestResult.wasSuccessful());<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br></span></div>
<br>可以看到，它将错误信息，异常信息保存在List或者Vector集合内，然后显示在界面上：<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;showErrorTrace()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;index</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;fFailureList.getSelectedIndex();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(index&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">-</span><span style="color: #000000;">1</span><span style="color: #000000;">)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">;<br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Throwable&nbsp;t</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;(Throwable)&nbsp;fExceptions.elementAt(index);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(fTraceFrame&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fTraceFrame</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;TraceFrame();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fTraceFrame.setLocation(</span><span style="color: #000000;">100</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">100</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fTraceFrame.showTrace(t);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fTraceFrame.setVisible(</span><span style="color: #0000ff;">true</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;showInfo(String&nbsp;message)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fStatusLine.setFont(PLAIN_FONT);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fStatusLine.setForeground(Color.black);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fStatusLine.setText(message);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;showStatus(String&nbsp;status)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fStatusLine.setFont(BOLD_FONT);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fStatusLine.setForeground(Color.red);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fStatusLine.setText(status);<br>&nbsp;&nbsp;&nbsp;&nbsp;}</span></div>
<br>而Junit中的目标对象(Subject）就是TestResult对象，它有添加观察者的方法：<br><br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #008000;">/**</span><span style="color: #008000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;Registers&nbsp;a&nbsp;TestListener<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">synchronized</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;addListener(TestListener&nbsp;listener)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fListeners.addElement(listener);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br></span></div>
<br>而通知观察者又是怎么做的呢？请看这几个方法，都是循环遍历观察者列表，并调用相应的更新方法：<br><br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #008000;">/**</span><span style="color: #008000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;Adds&nbsp;an&nbsp;error&nbsp;to&nbsp;the&nbsp;list&nbsp;of&nbsp;errors.&nbsp;The&nbsp;passed&nbsp;in&nbsp;exception&nbsp;caused&nbsp;the<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;error.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">synchronized</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;addError(Test&nbsp;test,&nbsp;Throwable&nbsp;t)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fErrors.addElement(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;TestFailure(test,&nbsp;t));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">for</span><span style="color: #000000;">&nbsp;(Enumeration&nbsp;e&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;fListeners.elements();&nbsp;e.hasMoreElements();)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;((TestListener)&nbsp;e.nextElement()).addError(test,&nbsp;t);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;Adds&nbsp;a&nbsp;failure&nbsp;to&nbsp;the&nbsp;list&nbsp;of&nbsp;failures.&nbsp;The&nbsp;passed&nbsp;in&nbsp;exception&nbsp;caused<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;the&nbsp;failure.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">synchronized</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;addFailure(Test&nbsp;test,&nbsp;AssertionFailedError&nbsp;t)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fFailures.addElement(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;TestFailure(test,&nbsp;t));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">for</span><span style="color: #000000;">&nbsp;(Enumeration&nbsp;e&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;fListeners.elements();&nbsp;e.hasMoreElements();)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;((TestListener)&nbsp;e.nextElement()).addFailure(test,&nbsp;t);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;Registers&nbsp;a&nbsp;TestListener<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">synchronized</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;addListener(TestListener&nbsp;listener)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fListeners.addElement(listener);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;Informs&nbsp;the&nbsp;result&nbsp;that&nbsp;a&nbsp;test&nbsp;was&nbsp;completed.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">synchronized</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;endTest(Test&nbsp;test)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">for</span><span style="color: #000000;">&nbsp;(Enumeration&nbsp;e&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;fListeners.elements();&nbsp;e.hasMoreElements();)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;((TestListener)&nbsp;e.nextElement()).endTest(test);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}</span></div>
<br><br>使用这个模式后带来的好处：<br>1)上面提到的Subject与Observer的抽象耦合，使JUnit可以支持不同的使用方式<br>2）支持了广播通信，目标对象不关心有多少对象对自己注册，它只是通知注册的观察者<br><br>最后，我实现了一个简单的ConsoleRunner，在控制台执行JUnit,比如我们写了一个简单测试：<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">package</span><span style="color: #000000;">&nbsp;junit.samples;<br><br></span><span style="color: #0000ff;">import</span><span style="color: #000000;">&nbsp;junit.framework.</span><span style="color: #000000;">*</span><span style="color: #000000;">;<br><br></span><span style="color: #008000;">/**</span><span style="color: #008000;"><br>&nbsp;*&nbsp;Some&nbsp;simple&nbsp;tests.<br>&nbsp;*&nbsp;<br>&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br></span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;SimpleTest&nbsp;</span><span style="color: #0000ff;">extends</span><span style="color: #000000;">&nbsp;TestCase&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">protected</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;fValue1;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">protected</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;fValue2;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;SimpleTest(String&nbsp;name)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">super</span><span style="color: #000000;">(name);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;setUp()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fValue1&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">2</span><span style="color: #000000;">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fValue2&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">3</span><span style="color: #000000;">;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;testAdd()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">double</span><span style="color: #000000;">&nbsp;result&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;fValue1&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;fValue2;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">assert</span><span style="color: #000000;">(result&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">5</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;testEquals()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assertEquals(</span><span style="color: #000000;">12</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">12</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assertEquals(</span><span style="color: #000000;">12L</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">12L</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assertEquals(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Long(</span><span style="color: #000000;">12</span><span style="color: #000000;">),&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Long(</span><span style="color: #000000;">12</span><span style="color: #000000;">));<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assertEquals(</span><span style="color: #000000;">"</span><span style="color: #000000;">Size</span><span style="color: #000000;">"</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">12</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">12</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assertEquals(</span><span style="color: #000000;">"</span><span style="color: #000000;">Capacity</span><span style="color: #000000;">"</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">12.0</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">11.99</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">0.01</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}</span></div>
<br>使用ConsoleRunner调用这个测试，代码很简单，不多做解释了：<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">package</span><span style="color: #000000;">&nbsp;net.rubyeye.junit.framework;<br><br></span><span style="color: #0000ff;">import</span><span style="color: #000000;">&nbsp;java.lang.reflect.Method;<br></span><span style="color: #0000ff;">import</span><span style="color: #000000;">&nbsp;java.util.ArrayList;<br></span><span style="color: #0000ff;">import</span><span style="color: #000000;">&nbsp;java.util.List;<br></span><span style="color: #0000ff;">import</span><span style="color: #000000;">&nbsp;java.util.Vector;<br><br></span><span style="color: #0000ff;">import</span><span style="color: #000000;">&nbsp;junit.framework.Test;<br></span><span style="color: #0000ff;">import</span><span style="color: #000000;">&nbsp;junit.framework.TestListener;<br></span><span style="color: #0000ff;">import</span><span style="color: #000000;">&nbsp;junit.framework.TestResult;<br></span><span style="color: #0000ff;">import</span><span style="color: #000000;">&nbsp;junit.samples.SimpleTest;<br></span><span style="color: #008000;">//</span><span style="color: #008000;">实现观察者接口</span><span style="color: #008000;"><br></span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;ConsoleRunner&nbsp;</span><span style="color: #0000ff;">implements</span><span style="color: #000000;">&nbsp;TestListener&nbsp;{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;TestResult&nbsp;fTestResult;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;Vector&nbsp;fExceptions;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;Vector&nbsp;fFailedTests;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;List&nbsp;fFailureList;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;ConsoleRunner()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fExceptions&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Vector();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fFailedTests&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Vector();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fFailureList&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;ArrayList();<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;endTest(Test&nbsp;test)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="color: #000000;">"</span><span style="color: #000000;">测试结束：</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;message&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;test.toString();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(fTestResult.wasSuccessful())<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(message&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;测试成功!</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">else</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(fTestResult.errorCount()&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">1</span><span style="color: #000000;">)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(message&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;had&nbsp;an&nbsp;error</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">else</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(message&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;had&nbsp;a&nbsp;failure</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">for</span><span style="color: #000000;">&nbsp;(</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;i&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">;&nbsp;i&nbsp;</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">&nbsp;fFailureList.size();&nbsp;i</span><span style="color: #000000;">++</span><span style="color: #000000;">)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(fFailureList.get(i));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">for</span><span style="color: #000000;">&nbsp;(</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;i&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">;&nbsp;i&nbsp;</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">&nbsp;fFailedTests.size();&nbsp;i</span><span style="color: #000000;">++</span><span style="color: #000000;">)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(fFailureList.get(i));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">for</span><span style="color: #000000;">&nbsp;(</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;i&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">;&nbsp;i&nbsp;</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">&nbsp;fExceptions.size();&nbsp;i</span><span style="color: #000000;">++</span><span style="color: #000000;">)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(fFailureList.get(i));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="color: #000000;">"</span><span style="color: #000000;">------------------------</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;startTest(Test&nbsp;test)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="color: #000000;">"</span><span style="color: #000000;">开始测试:</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;test);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;TestResult&nbsp;createTestResult()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;TestResult();<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;String&nbsp;truncateString(String&nbsp;s,&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;length)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(s.length()&nbsp;</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;length)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;s.substring(</span><span style="color: #000000;">0</span><span style="color: #000000;">,&nbsp;length)&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;"><img src="http://www.blogjava.net/Images/dot.gif"></span><span style="color: #000000;">"</span><span style="color: #000000;">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;s;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;addError(Test&nbsp;test,&nbsp;Throwable&nbsp;t)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(fTestResult.errorCount());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;appendFailure(</span><span style="color: #000000;">"</span><span style="color: #000000;">Error</span><span style="color: #000000;">"</span><span style="color: #000000;">,&nbsp;test,&nbsp;t);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;addFailure(Test&nbsp;test,&nbsp;Throwable&nbsp;t)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(fTestResult.failureCount());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;appendFailure(</span><span style="color: #000000;">"</span><span style="color: #000000;">Failure</span><span style="color: #000000;">"</span><span style="color: #000000;">,&nbsp;test,&nbsp;t);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;appendFailure(String&nbsp;kind,&nbsp;Test&nbsp;test,&nbsp;Throwable&nbsp;t)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;kind&nbsp;</span><span style="color: #000000;">+=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">:&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;test;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;msg&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;t.getMessage();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(msg&nbsp;</span><span style="color: #000000;">!=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;kind&nbsp;</span><span style="color: #000000;">+=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">:</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;truncateString(msg,&nbsp;</span><span style="color: #000000;">100</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fFailureList.add(kind);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fExceptions.addElement(t);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fFailedTests.addElement(test);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;go(String&nbsp;args[])&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Method[]&nbsp;methods&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;SimpleTest.</span><span style="color: #0000ff;">class</span><span style="color: #000000;">.getDeclaredMethods();<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">for</span><span style="color: #000000;">&nbsp;(</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;i&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">;&nbsp;i&nbsp;</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">&nbsp;methods.length;&nbsp;i</span><span style="color: #000000;">++</span><span style="color: #000000;">)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">取所有以test开头的方法</span><span style="color: #008000;"><br></span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(methods[i].getName().startsWith(</span><span style="color: #000000;">"</span><span style="color: #000000;">test</span><span style="color: #000000;">"</span><span style="color: #000000;">))&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Test&nbsp;test&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;SimpleTest(methods[i].getName());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fTestResult&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;createTestResult();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fTestResult.addListener(ConsoleRunner.</span><span style="color: #0000ff;">this</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">执行测试</span><span style="color: #008000;"><br></span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;test.run(fTestResult);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;main(String&nbsp;args[])&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;ConsoleRunner().go(args);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>}<br></span></div>
<br>  <img src ="http://www.blogjava.net/killme2008/aggbug/108743.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killme2008/" target="_blank">dennis</a> 2007-04-05 17:19 <a href="http://www.blogjava.net/killme2008/archive/2007/04/05/108743.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JUnit源码分析（一）——Command模式和Composite模式</title><link>http://www.blogjava.net/killme2008/archive/2007/04/05/108702.html</link><dc:creator>dennis</dc:creator><author>dennis</author><pubDate>Thu, 05 Apr 2007 07:10:00 GMT</pubDate><guid>http://www.blogjava.net/killme2008/archive/2007/04/05/108702.html</guid><wfw:comment>http://www.blogjava.net/killme2008/comments/108702.html</wfw:comment><comments>http://www.blogjava.net/killme2008/archive/2007/04/05/108702.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.blogjava.net/killme2008/comments/commentRss/108702.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/killme2008/services/trackbacks/108702.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; JUnit的源码相比于spring和hibernate来说比较简单，但麻雀虽小，五脏俱全，其中用到了比较多的设计模式。很多人已经在网上分享了他们对JUnit源码解读心得，我这篇小文谈不出什么新意，本来不打算写，可最近工作上暂时无事可做，那就写写吧，结合《设计模式》来看看。<br>&nbsp;&nbsp;&nbsp; 我读的是JUnit3.0的源码，目前JUnit已经发布到4.0版本了，尽管有比较大的改进，但基本的骨架不变，读3.0是为了抓住重点，省去对旁支末节的关注。我们来看看JUnit的核心代码，也就是Junit.framework包，除了4个辅助类（Assert,AssertFailedError,Protectable,TestFailure），剩下的就是我们需要重点关注的了。我先展示一张UML类图：<br><br><img alt="" src="http://www.blogjava.net/images/blogjava_net/killme2008/junit_core.jpg">&nbsp;&nbsp;&nbsp; 我们先不去关注TestDecorator类（此处是Decorator模式，下篇文章再讲），看看Test接口，以及它的两个实现类TestCase和TestSuite。很明显，此处用到了Command模式，为什么要使用这个模式呢？让我们先来看看什么是Command模式。<br><br><span style="font-weight: bold;">Command模式<br><br></span>Command模式是行为型模式之一<br><br>1.意图：将一个请求封装为一个对象，从而使你可用不同的请求对客户进行参数化；对请求排队或者记录请求日志，以及支持可撤销的操作。<br>2.适用场景：<br>1）抽象出待执行的动作以参数化对象，Command模式是回调函数的面向对象版本。回调函数，我想大家都明白，函数在某处注册，然后在稍后的某个时候被调用。<br>2）可以在不同的时刻指定、排列和执行请求。<br>3）支持修改日志，当系统崩溃时，这些修改可以被重做一遍。<br>4）通过Command模式，你可以通过一个公共接口调用所有的事务，并且也易于添加新的事务。<br><br><br>3。UML图：<br>&nbsp;&nbsp;&nbsp; <img alt="" src="http://www.blogjava.net/images/blogjava_net/killme2008/command.jpg" height="275" width="408"><br><br>4.效果：<br>1）命令模式将调用操作的对象与如何实现该操作的对象解耦。<br>2）将命令当成一个头等对象，它们可以像一般对象那样进行操纵和扩展<br>3）可以将多个命令复合成一个命令，与Composite模式结合使用<br>4）增加新的命令很容易，隔离对现有类的影响<br>5）可以与备忘录模式配合，实现撤销功能。<br><br>&nbsp;&nbsp;&nbsp; 在了解了Command模式之后，那我们来看JUnit的源码，Test接口就是命令的抽象接口，而TestCase和TestSuite是具体的命令<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #008000;">//</span><span style="color: #008000;">抽象命令接口</span><span style="color: #008000;"><br></span><span style="color: #0000ff;">package</span><span style="color: #000000;">&nbsp;junit.framework;<br><br></span><span style="color: #008000;">/**</span><span style="color: #008000;"><br>&nbsp;*&nbsp;A&nbsp;&lt;em&gt;Test&lt;/em&gt;&nbsp;can&nbsp;be&nbsp;run&nbsp;and&nbsp;collect&nbsp;its&nbsp;results.<br>&nbsp;*<br>&nbsp;*&nbsp;</span><span style="color: #808080;">@see</span><span style="color: #008000;">&nbsp;TestResult<br>&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br></span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">interface</span><span style="color: #000000;">&nbsp;Test&nbsp;{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;Counts&nbsp;the&nbsp;number&nbsp;of&nbsp;test&nbsp;cases&nbsp;that&nbsp;will&nbsp;be&nbsp;run&nbsp;by&nbsp;this&nbsp;test.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">abstract</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;countTestCases();<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;Runs&nbsp;a&nbsp;test&nbsp;and&nbsp;collects&nbsp;its&nbsp;result&nbsp;in&nbsp;a&nbsp;TestResult&nbsp;instance.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">abstract</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;run(TestResult&nbsp;result);<br>}<br><br></span><span style="color: #008000;">//</span><span style="color: #008000;">具体命令一</span><span style="color: #008000;"><br></span><span style="color: #000000;"><br></span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">abstract</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;TestCase&nbsp;</span><span style="color: #0000ff;">extends</span><span style="color: #000000;">&nbsp;Assert&nbsp;</span><span style="color: #0000ff;">implements</span><span style="color: #000000;">&nbsp;Test&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;the&nbsp;name&nbsp;of&nbsp;the&nbsp;test&nbsp;case<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">final</span><span style="color: #000000;">&nbsp;String&nbsp;fName;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br>&nbsp;&nbsp;&nbsp; <img src="http://www.blogjava.net/Images/dot.gif"><img src="http://www.blogjava.net/Images/dot.gif"><br><br>//具体命令二<br><br>public&nbsp;class&nbsp;TestSuite&nbsp;implements&nbsp;Test&nbsp;{<br>&nbsp;&nbsp;&nbsp; &nbsp;<img src="http://www.blogjava.net/Images/dot.gif"></span></div>
<br>由此带来的好处：<br>1.客户无需使用任何条件语句去判断测试的类型，可以用统一的方式调用测试和测试套件，解除了客户与具体测试子类的耦合<br>2.如果要增加新的TestCase也很容易，实现Test接口即可，不会影响到其他类。<br>3.很明显，TestSuite是通过组合多个TestCase的复合命令，这里使用到了Composite模式（组合）<br>4.尽管未实现redo和undo操作，但将来也很容易加入并实现。<br><br>&nbsp;&nbsp;&nbsp; 我们上面说到TestSuite组合了多个TestCase，应用到了Composite模式，那什么是Composite模式呢？具体来了解下。<br><br><span style="font-weight: bold;">Composite模式<br><br></span>composite模式是对象结构型模式之一。<span style="font-weight: bold;"><br></span>1.意图：将对象组合成树形结构以表示&#8220;部分——整体&#8221;的层次结构。使得用户对单个对象和组合结构的使用具有一致性。<br><br>2.适用场景：<br>1）想表示对象的部分-整体层次<br>2）希望用户能够统一地使用组合结构和单个对象。具体到JUnit源码，我们是希望用户能够统一地方式使用TestCase和TestSuite<br><br>3.UML图：<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <img alt=""  src="http://www.blogjava.net/images/blogjava_net/killme2008/composite.gif"><br><br>图中单个对象就是树叶（Leaf），而组合结构就是Compoiste，它维护了一个Leaf的集合。而Component是一个抽象角色，给出了共有接口和默认行为，也就是JUnit源码中的Test接口。<br><br>4.效果：<br>1）定义了基本对象和组合对象的类层次结构，通过递归可以产生更复杂的组合对象<br>2）简化了客户代码，客户可以使用一致的方式对待单个对象和组合结构<br>3）添加新的组件变的很容易。但这个会带来一个问题，你无法限制组件中的组件，只能靠运行时的检查来施加必要的约束条件<br><br>&nbsp;&nbsp;&nbsp; 具体到JUnit源码，单个对象就是TestCase，而复合结构就是TestSuite，Test是抽象角色只有一个run方法。TestSuite维护了一个TestCase对象的集合fTests：<br><br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;Vector&nbsp;fTests</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Vector(</span><span style="color: #000000;">10</span><span style="color: #000000;">);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;Adds&nbsp;a&nbsp;test&nbsp;to&nbsp;the&nbsp;suite.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;addTest(Test&nbsp;test)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fTests.addElement(test);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br></span><span style="color: #008000;">&nbsp;&nbsp;&nbsp; /**</span><span style="color: #008000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;Runs&nbsp;the&nbsp;tests&nbsp;and&nbsp;collects&nbsp;their&nbsp;result&nbsp;in&nbsp;a&nbsp;TestResult.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;run(TestResult&nbsp;result)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">for</span><span style="color: #000000;">&nbsp;(Enumeration&nbsp;e</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;tests();&nbsp;e.hasMoreElements();&nbsp;)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(result.shouldStop()&nbsp;)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">break</span><span style="color: #000000;">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Test&nbsp;test</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;(Test)e.nextElement();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;test.run(result);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}</span></div>
<br>当执行run方法时遍历这个集合，调用里面每个TestCase对象的run()方法，从而执行测试。我们使用的时候仅仅需要把TestCase添加到集合内，然后用一致的方式（run方法）调用他们进行测试。<br><br>考虑使用Composite模式之后带来的好处：<br>1）JUnit可以统一地处理组合结构TestSuite和单个对象TestCase，避免了条件判断，并且可以递归产生更复杂的测试对象<br>2）很容易增加新的TestCase。<br><br><br>参考资料：《设计模式——可复用面向对象软件的基础》<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 《JUnit设计模式分析》 刘兵<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JUnit源码和文档<br><br><br><br><span style="font-weight: bold;"></span><br><br><br><br><br><span style="font-weight: bold;"></span><br>&nbsp;&nbsp;&nbsp;&nbsp; <br><img src ="http://www.blogjava.net/killme2008/aggbug/108702.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killme2008/" target="_blank">dennis</a> 2007-04-05 15:10 <a href="http://www.blogjava.net/killme2008/archive/2007/04/05/108702.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>创建型模式摘记</title><link>http://www.blogjava.net/killme2008/archive/2007/03/17/104485.html</link><dc:creator>dennis</dc:creator><author>dennis</author><pubDate>Sat, 17 Mar 2007 09:01:00 GMT</pubDate><guid>http://www.blogjava.net/killme2008/archive/2007/03/17/104485.html</guid><wfw:comment>http://www.blogjava.net/killme2008/comments/104485.html</wfw:comment><comments>http://www.blogjava.net/killme2008/archive/2007/03/17/104485.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/killme2008/comments/commentRss/104485.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/killme2008/services/trackbacks/104485.html</trackback:ping><description><![CDATA[    系统的演化应当依赖于组合，而不是继承；这就提出了将类的实例化委托给一个对象的要求，因此创建型模式将变的越来越重要。<br /><div style="text-indent: 21pt;">创建型模式属于对象创建模型。所谓对象创建模型就是说将实例化的工作委托给另一个对象来做。与之相对应的是类创建模型，这是一种通过继承改变被实例化的类。</div><div><span>       </span>创建型模式有两个重要的特点：</div><div style="margin-left: 39pt; text-indent: -18pt;"><span>1） </span>客户不知道创建的具体类是什么（除非看源代码）</div><div style="margin-left: 39pt; text-indent: -18pt;"><span>2） </span>隐藏了类的实例是如何被创建和放在一起的</div><br />一。抽象工厂模式<br />1.意图：提供一个创建<b>一系列</b>相关或相互依赖对象的接口，而无需指定它们的具体的类。<br />2.适用场景：<br />1)一个系统要独立于它的产品的创建、组合和表示时<br />2）一个系统要由多个产品系列中的一个来配置时<br />3）当你要强调一系列相关的产品对象的设计以便进行联合使用时<br />4）当你提供一个产品类库，而只想显示它们的接口而不是实现时<br /><br />3.UML图——结构<br /><img src="http://p.blog.csdn.net/images/p_blog_csdn_net/sheismylife/07cbd5dfdbdd4027a1ed513e503b1d6f.png" /><br />4.效果：<br />1）分离了具体的类，通过抽象接口将客户与具体的类分离<br />2）易于交换产品系列<br />3）有利于产品的一致性<br />4）难以支持新种类的产品，比如我们现在有一个ProductC产品，我们需要增加类AbstractProductC，增加AbstractFactory::
CreanteProductC方法，并且两个产品系列的实际创建者ConCreateFactory1、ConCreateFactor2都要实现该方
法。<div>可以通过给方法加参数的方式来指明创建的是什么产品，这样客户代码就无需改变，只要传递不同的参数。AbstractFactory类只需要提供一个CreateProduct(const string&amp; name)方法即可。<br /><br />5.代码实现，以《深入浅出设计模式(java C#)》的动物工厂为例：<br /><div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; background-color: rgb(238, 238, 238); font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: rgb(0, 0, 255);">using</span><span style="color: rgb(0, 0, 0);"> System;<br /></span><span style="color: rgb(0, 0, 255);">namespace</span><span style="color: rgb(0, 0, 0);"> AnimalWorld<br />{<br />    </span><span style="color: rgb(0, 128, 0);">//</span><span style="color: rgb(0, 128, 0);"> 抽象大陆工厂</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);">abstract</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">class</span><span style="color: rgb(0, 0, 0);"> ContinentFactory<br />    {<br />        </span><span style="color: rgb(0, 0, 255);">abstract</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);"> Herbivore CreateHerbivore();<br />        </span><span style="color: rgb(0, 0, 255);">abstract</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);"> Carnivore CreateCarnivore();<br />    }<br />    </span><span style="color: rgb(0, 128, 0);">//</span><span style="color: rgb(0, 128, 0);">非洲大陆,有角马，狮子</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);">class</span><span style="color: rgb(0, 0, 0);"> AfricaFactory : ContinentFactory<br />    {<br />        </span><span style="color: rgb(0, 0, 255);">override</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);"> Herbivore CreateHerbivore()<br />        {<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, 255);">new</span><span style="color: rgb(0, 0, 0);"> Wildebeest();<br />        }<br />        </span><span style="color: rgb(0, 0, 255);">override</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);"> Carnivore CreateCarnivore()<br />        {<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, 255);">new</span><span style="color: rgb(0, 0, 0);"> Lion();<br />        }<br />    }<br />    </span><span style="color: rgb(0, 128, 0);">//</span><span style="color: rgb(0, 128, 0);"> 美洲大陆,有狼,野牛</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);">class</span><span style="color: rgb(0, 0, 0);"> AmericaFactory : ContinentFactory<br />    {<br />        </span><span style="color: rgb(0, 0, 255);">override</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);"> Herbivore CreateHerbivore()<br />        {<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, 255);">new</span><span style="color: rgb(0, 0, 0);"> Bison();<br />        }<br />        </span><span style="color: rgb(0, 0, 255);">override</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);"> Carnivore CreateCarnivore()<br />        {<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, 255);">new</span><span style="color: rgb(0, 0, 0);"> Wolf();<br />        }<br />    }<br />    </span><span style="color: rgb(0, 128, 0);">//</span><span style="color: rgb(0, 128, 0);">食草动物"</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);">abstract</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">class</span><span style="color: rgb(0, 0, 0);"> Herbivore<br />    {<br />    }<br />    </span><span style="color: rgb(0, 128, 0);">//</span><span style="color: rgb(0, 128, 0);">肉食动物"</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);">abstract</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">class</span><span style="color: rgb(0, 0, 0);"> Carnivore<br />    {<br />        </span><span style="color: rgb(0, 128, 0);">//</span><span style="color: rgb(0, 128, 0);">猎食食草动物的方法</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);">abstract</span><span style="color: rgb(0, 0, 0);"> </span><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);"> Eat( Herbivore h );<br />    }<br />    </span><span style="color: rgb(0, 128, 0);">//</span><span style="color: rgb(0, 128, 0);">角马</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);">class</span><span style="color: rgb(0, 0, 0);"> Wildebeest : Herbivore<br />    {<br />    }<br />    </span><span style="color: rgb(0, 128, 0);">//</span><span style="color: rgb(0, 128, 0);">狮子"</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);">class</span><span style="color: rgb(0, 0, 0);"> Lion : Carnivore<br />    {<br />        </span><span style="color: rgb(0, 128, 0);">//</span><span style="color: rgb(0, 128, 0);">重载猎食食草动物的方法</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);">override</span><span style="color: rgb(0, 0, 0);"> </span><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);"> Eat( Herbivore h )<br />        {<br />            Console.WriteLine( </span><span style="color: rgb(0, 0, 255);">this</span><span style="color: rgb(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);">"</span><span style="color: rgb(0, 0, 0);"> eats </span><span style="color: rgb(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);"> h );<br />        }<br />    }<br />    </span><span style="color: rgb(0, 128, 0);">//</span><span style="color: rgb(0, 128, 0);">野牛</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);">class</span><span style="color: rgb(0, 0, 0);"> Bison : Herbivore<br />    {<br />    }<br />    </span><span style="color: rgb(0, 128, 0);">//</span><span style="color: rgb(0, 128, 0);">狼</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);">class</span><span style="color: rgb(0, 0, 0);"> Wolf : Carnivore<br />    {<br />        </span><span style="color: rgb(0, 128, 0);">//</span><span style="color: rgb(0, 128, 0);">重载猎食食草动物的方法</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);">override</span><span style="color: rgb(0, 0, 0);"> </span><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);"> Eat( Herbivore h )<br />        {<br />            Console.WriteLine( </span><span style="color: rgb(0, 0, 255);">this</span><span style="color: rgb(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);">"</span><span style="color: rgb(0, 0, 0);"> eats </span><span style="color: rgb(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);"> h );<br />        }<br />    }<br />    </span><span style="color: rgb(0, 128, 0);">//</span><span style="color: rgb(0, 128, 0);">动物世界类</span><span style="col