﻿<?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-Sealyu-随笔分类-设计模式</title><link>http://www.blogjava.net/sealyu/category/30679.html</link><description>--- The devil's in the Details</description><language>zh-cn</language><lastBuildDate>Fri, 11 Jun 2010 18:12:13 GMT</lastBuildDate><pubDate>Fri, 11 Jun 2010 18:12:13 GMT</pubDate><ttl>60</ttl><item><title>UML设计的9种图例(转)</title><link>http://www.blogjava.net/sealyu/archive/2010/05/20/321428.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Wed, 19 May 2010 17:31:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2010/05/20/321428.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/321428.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2010/05/20/321428.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/321428.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/321428.html</trackback:ping><description><![CDATA[<font size="2" face="Courier New"><span>对
UML不是很了解，简单得了解一下UML设计中有的图例及基本作用。首先对UML中的各个图的功用做一个简单介绍：</span>
</font>
<div>
&nbsp;</div>
<div>
<font size="2" face="Courier New">
<strong>1、用例图</strong>
</font>
</div>
<div>
&nbsp;</div>
<div>
<font size="2" face="Courier New">&nbsp;&nbsp;&nbsp;
说明的是谁要使用系统，以及他们使用该系统可以做些什么。一个用例图包含了多个模型元素，如系统、参与者和用例，并且显示了这些元素之间的各种关系，如泛
化、关联和依赖。</font>
</div>
<div>
&nbsp;</div>
<div>
<font size="2" face="Courier New">
<strong>2、类图</strong>
</font>
</div>
<div>
&nbsp;</div>
<div>
<font size="2" face="Courier New">&nbsp;&nbsp;&nbsp;
能够让我们在正确编写代码以前对系统有一个全面的认识。类图是一种模型类型，确切的说，是一种静态模型类型。一个类图根据系统中的类，以及各个类之间的关
系描述系统的静态视图。</font>
</div>
<div>
&nbsp;</div>
<div>
<font size="2" face="Courier New">
<strong>3、对象图</strong>
</font>
</div>
<div>
&nbsp;</div>
<div>
<font size="2" face="Courier New">&nbsp;&nbsp;&nbsp;
与类图极为相似，只是它描述的不是类之间的关系，而是对象之间的关系</font>
</div>
<div>
&nbsp;</div>
<div>
<font size="2" face="Courier New">
<strong>4、活动图</strong>
</font>
</div>
<div>
&nbsp;</div>
<div>
<font size="2" face="Courier New">&nbsp;&nbsp;&nbsp;
能够演示出系统中哪些地方存在功能，以及这些功能和系统中其他组件的功能如何共同满足前面使用用例图建模的商务需求。</font>
</div>
<div>
&nbsp;</div>
<div>
<font size="2" face="Courier New">
<strong>5、状态图</strong>
</font>
</div>
<div>
&nbsp;</div>
<div>
<font size="2" face="Courier New">&nbsp;&nbsp;&nbsp;
可以捕获对象、子系统和系统的生命周期。他们可以告知一个对象可以拥有的状态，并且事件(如小溪的接收、时间的流逝、错误、条件变为真等)会怎么随着时间
的推移来影响这些状态。一个状态图应该连接到所有具有清晰的可标识状态和复杂行为的类；该图可以确定类的行为，以及该行为如何根据当前的状态变化，也可以
展示哪些事件将会改变类的对象的状态。</font>
</div>
<div>
&nbsp;</div>
<div>
<font size="2" face="Courier New">
<strong>6、顺序图</strong>
</font>
</div>
<div>
&nbsp;</div>
<div>
<font size="2" face="Courier New">&nbsp;&nbsp;&nbsp;
是用来显示你的参与者如何以一系列顺序的步骤与系统的对象交互的模型。顺序图可以用来展示对象之间是如何进行交互的。顺序图将显示的重点放在消息序列上，
即消息是如何在对象之间被发送和接收的。</font>
</div>
<div>
&nbsp;</div>
<div>
<font size="2" face="Courier New">
<strong>7、协作图</strong>
</font>
</div>
<div>
&nbsp;</div>
<div>
<font size="2" face="Courier New">&nbsp;&nbsp;&nbsp;
可以看成是类图和顺序图的交集，协作图建模对象或者角色，以及它们彼此之间是如何通信的。</font>
</div>
<div>
&nbsp;</div>
<div>
<font size="2" face="Courier New">
<strong>8、构件图</strong>
</font>
</div>
<div>
&nbsp;</div>
<div>
<font size="2" face="Courier New">&nbsp;&nbsp;&nbsp;
用来建模软件的组件及其相互之间的关系，这些图由构件标记符和构件之间的关系构成。在组件图中，构件时软件单个组成部分，它可以是一个文件，产品、可执行
文件和脚本等。</font>
</div>
<div>
&nbsp;</div>
<div>
<font size="2" face="Courier New">
<strong>9、部署图</strong>
</font>
</div>
<div>
&nbsp;</div>
<div>
<font size="2" face="Courier New">&nbsp;&nbsp;&nbsp;
是用来建模系统的物理部署。例如计算机和设备，以及它们之间是如何连接的。部署图的使用者是开发人员、系统集成人员和测试人员。</font>
</div>
<div>
&nbsp;</div>
<div>
&nbsp;</div>
<div>
<font size="2" face="Courier New">
-------------------------------------------------------------------------------------------------------------
</font>
</div>
<div>
&nbsp;</div>
<div>
<font size="2" face="Courier New">
&nbsp;&nbsp;&nbsp; 然后转一篇blog对各个图做一个比较详细的说明和举例：
</font>
</div>
<div>
&nbsp;</div>
<div>
<font size="2" face="Courier New">
**********************************************************
</font>
</div>
<div>
<font size="2" face="Courier New">
<span>
<strong>
<font size="3">UML(2009-04-11 12:18:57)</font>
</strong>
<br />
标签：uml 状态图 类图 it&nbsp;&nbsp; 分类：软件设计 </span>
</font>
</div>
<div>
&nbsp;</div>
<div>
<font size="2" face="Courier New">
<a href="http://blog.sina.com.cn/s/blog_5f240fc40100cnm2.html">http://blog.sina.com.cn/s/blog_5f240fc40100cnm2.html</a>
</font>
</div>
<div>
<span>
<font size="2" face="Courier New">**********************************************************</font>
</span>
</div>
<div>
&nbsp;</div>
<div>
<font size="2" face="Courier New">
<span>UML(统一建模语言)：是面向对象的可视化建模的一种语言。是数据库
设计过程中，在E-R图（实体-联系图）的设计后的进一步建模。<br />
UML中有3种构造块：事物、关系和图，事物是对模型中最具有代表性的成分的抽
象；关系是把事物结合在一起；图聚集了相关的的事物。具体关系图标如下：</span>
</font>
</div>
<div>
&nbsp;</div>
<span>
<div>
<font size="2" face="Courier New">
<img alt="uml01.jpg" src="../../images/blogjava_net/wxqxs/upload3/uml01.jpg" width="413" height="448" border="0" />
</font>
</div>
<div>
<br />
<font face="Courier New">
<font size="2">
<strong>说明：<br />
</strong>构件事物是名词，是模型的静态部分。<br />
行为事物是动态部分，表示行为。<br />
分
组事物是组织部分。<br />
注释事物是解释部分。</font>
</font>
</div>
<div>
&nbsp;</div>
<div>
<font size="2" face="Courier New">依赖：一个事物变化会引起另一个事物变化。<br />
聚集：特殊的关
联，描述整体与部分的组合关系。<br />
泛化：是一种特殊与一般的关系，如子元素（特殊）与父元素（一般），箭头指向父元素。<br />
实现：类元之间的关
系，其中一个类元指定了由另一个类元保证执行的契约。一般用在接口和实现他们的类之间或用例和实现它们的协作之间。</font>
</div>
<div>
&nbsp;</div>
<div>
<font size="2" face="Courier New">UML提供9种视图：类图、对象图，用例图，序列图、协作图，状态
图、活动图，构件图和部署图。</font>
</div>
<div>
<br />
<font size="2" face="Courier New">在UML系统开发中有三个主要的模型：<br />
</font>
</div>
<div>
<font size="2" face="Courier New">功能模型: 从用户的角度展示系统的功能，包括用例图。<br />
对象
模型: 采用对象，属性，操作，关联等概念展示系统的结构和基础，包括类图。<br />
动态模型: 展现系统的内部行为。 包括序列图，活动图，状态图。</font>
</div>
<div>
&nbsp;</div>
<div>
<br />
<font size="2" face="Courier New">下面具体说明：</font>
</div>
<div>
<br />
<font face="Courier New">
<font size="2">
<strong>1.类图</strong>：描述一组对象、接口、协作等事物之间的关系。如下图(摘自网络)：</font>
</font>
</div>
<div>
&nbsp;</div>
<div>
<font size="2" face="Courier New">
<img alt="uml02.jpg" src="../../images/blogjava_net/wxqxs/upload3/uml02.jpg" width="500" height="421" border="0" />
</font>
</div>
<div>
&nbsp;</div>
<div>
<font size="2" face="Courier New">&nbsp;注：#表示protected，+表示Public，-表示
private<br />
<br />
<br />
</font>
</div>
<div>
<font face="Courier New">
<font size="2">
<strong>2.对象图</strong>：描述一组对象之间的关系，是具有具体属性值和行为的一个具体事物，其是类图中所建事
物实例的静态快照，其与类图的主要区别是一个是抽象的,而对象图是具体的。如下图(摘自网络)：</font>
</font>
</div>
<div>
&nbsp;</div>
<div>
<font size="2" face="Courier New">
<img alt="uml03.jpg" src="../../images/blogjava_net/wxqxs/upload3/uml03.jpg" width="500" height="238" border="0" />
</font>
</div>
<div>
<br />
<font face="Courier New">
<font size="2">
<strong>3.用例图</strong>：描述一组用例、参与者以及它们之间的关系，其展示的是该系统在它的外面环境中所提供
的外部可见服务。如下图(摘自网络)：</font>
</font>
</div>
<div>
&nbsp;</div>
<div>
<font size="2" face="Courier New">
<img alt="uml04.jpg" src="../../images/blogjava_net/wxqxs/upload3/uml04.jpg" width="500" height="358" border="0" />
</font>
</div>
<div>
<br />
<font face="Courier New">
<font size="2">
<strong>4.交互图</strong>：包括<strong>序列图</strong>（<font color="#ff0000">顺序图</font>）和<strong>协作图</strong>，两者对应，顺序图是强调消息时间顺序，有对象生命
线和控制焦点。协作图是强调接收和发送消息的对象的结构组织，有路径和顺序号。如下图(摘自网络)：</font>
</font>
</div>
<div>
&nbsp;</div>
<div>
<font size="2" face="Courier New">序列图：</font>
</div>
<div>
&nbsp;</div>
<div>
<font size="2" face="Courier New">
<img alt="uml05.jpg" src="../../images/blogjava_net/wxqxs/upload3/uml05.jpg" width="437" height="397" border="0" />
</font>
</div>
<div>
&nbsp;</div>
<div>
<font size="2" face="Courier New">协作图：</font>
</div>
<div>
&nbsp;</div>
<div>
<font size="2" face="Courier New">
<img alt="uml06.jpg" src="../../images/blogjava_net/wxqxs/upload3/uml06.jpg" width="500" height="301" border="0" />
</font>
</div>
<div>
<br />
<font face="Courier New">
<font size="2">
<strong>5.状态图</strong>：展示了一个状态机，由状态、转换、事件和活动组成。强调事件行为的顺序。如下图(摘
自网络)：</font>
</font>
</div>
<div>
&nbsp;</div>
<div>
<font size="2" face="Courier New">
<img alt="uml07.jpg" src="../../images/blogjava_net/wxqxs/upload3/uml07.jpg" width="500" height="213" border="0" />
</font>
</div>
<div>
&nbsp;</div>
<div>
<font face="Courier New">
<font size="2">
<strong>6.活动图</strong>：是一种特殊的状态图，实现一个活动到另一个活动的流程。如下图(摘自网络)：</font>
</font>
</div>
<div>
&nbsp;</div>
<div>
<font size="2" face="Courier New">
<img alt="uml08.jpg" src="../../images/blogjava_net/wxqxs/upload3/uml08.jpg" width="500" height="405" border="0" />
</font>
</div>
<div>
&nbsp;</div>
<div>
<font face="Courier New">
<font size="2">
<strong>7.构件图和部署图</strong>：构件图展示一组构件之间的组织和依赖关系，并以全局的模型展示出来。部署图
是构件的配置及描述系统如何在硬件上部署。如下图(摘自网络)：</font>
</font>
</div>
<div>
&nbsp;</div>
<div>
<font size="2" face="Courier New">
<img alt="uml09.jpg" src="../../images/blogjava_net/wxqxs/upload3/uml09.jpg" width="500" height="115" border="0" />
</font>
</div>
<div>
&nbsp;</div>
<div>
<font size="2" face="Courier New">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;
如有不足，请指正&gt;<br />
</font>
</div>
</span>
<div>
<font size="2" face="Courier New">
------------------------------------------------------------------------------------------
</font>
</div>
<div>
&nbsp;</div>
<div>
<font size="2" face="Courier New">
<span>&nbsp;&nbsp;&nbsp;
找齐这么多的图真的不容易啊，我试了一下太麻烦就放弃了，对这个作者表示钦佩。</span>
</font>
</div>
<div>
&nbsp;</div>
<div>
<font size="2" face="Courier New">
<span>&nbsp;&nbsp;&nbsp;
好了，其他如果需要更具体的介绍，可以看下面这个地址，说的比较详细：</span>
</font>
<font size="2" face="Courier New">
<span>
<a href="http://www.cnblogs.com/macou/archive/2009/02/18/1392903.html">http://www.cnblogs.com/macou/archive/2009/02/18/1392903.html</a>
</span>
</font>
</div>
<div>
&nbsp;</div>
<div>
&nbsp;</div>
<div>
&nbsp;</div>
<br />
<br />
<p style="font-family: Comic Sans MS;" align="left">
<a style="font-size: 8pt;" href="../../wxqxs/">
<font style="font-size: 8pt;" size="3">
<br />
</font>
</a>
<br />
-The End-</p>
<img src ="http://www.blogjava.net/sealyu/aggbug/321428.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2010-05-20 01:31 <a href="http://www.blogjava.net/sealyu/archive/2010/05/20/321428.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>The Tao Of Programming</title><link>http://www.blogjava.net/sealyu/archive/2010/05/08/320337.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Fri, 07 May 2010 21:52:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2010/05/08/320337.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/320337.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2010/05/08/320337.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/320337.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/320337.html</trackback:ping><description><![CDATA[<p> Translated by Geoffrey James
</p>
<p> Transcribed by Duke Hillard
</p>
<p> Transmitted by Anupam Trivedi, Sajitha Tampi, and Meghshyam
Jagannath
</p>
<p> Re-html-ized and edited by Kragen Sittler
</p>
<p> <em>Last modified 1996-04-10 or earlier</em>
</p>
<hr />
<h2 align="right"> Table of Contents </h2>
<ol>
    <li>     <a href="http://www.canonical.org/%7Ekragen/tao-of-programming.html#book1">The
    Silent Void</a>
    </li>
    <li>     <a href="http://www.canonical.org/%7Ekragen/tao-of-programming.html#book2">The
    Ancient Masters</a>
    </li>
    <li>     <a href="http://www.canonical.org/%7Ekragen/tao-of-programming.html#book3">Design</a>
    </li>
    <li>     <a href="http://www.canonical.org/%7Ekragen/tao-of-programming.html#book4">Coding</a>
    </li>
    <li>     <a href="http://www.canonical.org/%7Ekragen/tao-of-programming.html#book5">Maintenance</a>
    </li>
    <li>     <a href="http://www.canonical.org/%7Ekragen/tao-of-programming.html#book6">Management</a>
    </li>
    <li>     <a href="http://www.canonical.org/%7Ekragen/tao-of-programming.html#book7">Corporate
    Wisdom</a>
    </li>
    <li>     <a href="http://www.canonical.org/%7Ekragen/tao-of-programming.html#book8">Hardware
    and Software</a>
    </li>
    <li>     <a href="http://www.canonical.org/%7Ekragen/tao-of-programming.html#book9">Epilogue</a>
    </li>
</ol>
<hr />
<h2 align="right"><a name="book1">Book 1 - The Silent Void </a></h2>
<p> Thus spake the master programmer:
</p>
<p> ``When you have learned to snatch the error code from the trap
frame, it
will be time for you to leave.''
</p>
<h3 align="center">                                 1.1</h3>
<p> Something mysterious is formed, born in the silent void.  Waiting
alone
and unmoving, it is at once still and yet in constant motion.  It is
the source of all programs.  I do not know its name, so I will call it
the Tao of Programming.
</p>
<p> If the Tao is great, then the operating system is great.  If the
operating system is great, then the compiler is great.  If the compiler
is great, then the application is great.  The user is pleased and there
exists harmony in the world.
</p>
<p> The Tao of Programming flows far away and returns on the wind of
morning.
</p>
<h3 align="center">                                 1.2</h3>
<p> The Tao gave birth to machine language.  Machine language gave birth
to
the assembler.
</p>
<p> The assembler gave birth to the compiler.  Now there are ten
thousand
languages.
</p>
<p> Each language has its purpose, however humble.  Each language
expresses
the Yin and Yang of software.  Each language has its place within the
Tao.
</p>
<p> But do not program in <tt>COBOL</tt> if you can avoid it.
</p>
<h3 align="center">                                 1.3</h3>
<p> In the beginning was the Tao. The Tao gave birth to Space and Time.
Therefore Space and Time are Yin and Yang of programming.
</p>
<p> Programmers that do not comprehend the Tao are always running
out of
time and space for their programs. Programmers that comprehend the
Tao always have enough time and space to accomplish their goals.
</p>
<p> How could it be otherwise?
</p>
<h3 align="center">                                 1.4</h3>
<p> The wise programmer is told about Tao and follows it. The average
programmer is told about Tao and searches for it. The foolish
programmer is told about Tao and laughs at it.
</p>
<p> If it were not for laughter, there would be no Tao.
</p>
<p> The highest sounds are hardest to hear. <br />
Going forward is a way to retreat. <br />
Great talent shows itself late in life. <br />
Even a perfect program still has bugs. <br />
</p>
<hr />
<h2 align="right"><a name="book2"> Book 2 - The Ancient Masters</a></h2>
<p> Thus spake the master programmer:
</p>
<p> ``After three days without programming, life becomes
meaningless.''
</p>
<h3 align="center">                                 2.1</h3>
<p> The programmers of old were mysterious and profound.  We cannot
fathom
their thoughts, so all we do is describe their appearance.
</p>
<p> Aware, like a fox crossing the water.  Alert, like a general on
the
battlefield. Kind, like a hostess greeting her guests.  Simple, like
uncarved blocks of wood.  Opaque, like black pools in darkened caves.
</p>
<p> Who can tell the secrets of their hearts and minds?
</p>
<p> The answer exists only in Tao.
</p>
<h3 align="center">                                 2.2</h3>
<p> Grand Master Turing once dreamed that he was a machine.  When he
awoke
he exclaimed:
</p>
<blockquote>
``I don't know whether I am Turing dreaming that I am a machine,
or a machine dreaming that I am Turing!''
</blockquote>
<h3 align="center">                                 2.3</h3>
<p> A programmer from a very large computer company went to a software
conference and then returned to report to his manager, saying:  ``What
sort of programmers work for other companies?  They behaved badly and
were unconcerned with appearances.  Their hair was long and unkempt
and their clothes were wrinkled and old.  They crashed our hospitality
suite and they made rude noises during my presentation.''
</p>
<p> The manager said:  ``I should have never sent you to the
conference.
Those programmers live beyond the physical world. They consider life
absurd, an accidental coincidence. They come and go without knowing
limitations. Without a care, they live only for their programs. Why
should they bother with social conventions?
</p>
<p> ``They are alive within the Tao.''
</p>
<h3 align="center">                                 2.4</h3>
<p> A novice asked the Master:  ``Here is a programmer that never
designs,
documents or tests his programs.  Yet all who know him consider him
one of the best programmers in the world.  Why is this?''
</p>
<p> The Master replies:  ``That programmer has mastered the Tao.  He
has
gone beyond the need for design; he does not become angry when the
system crashes, but accepts the universe without concern.  He has gone
beyond the need for documentation; he no longer cares if anyone else
sees his code.  He has gone beyond the need for testing; each of his
programs are perfect within themselves, serene and elegant, their
purpose self-evident.  Truly, he has entered the mystery of Tao.''
</p>
<hr />
<h2 align="right"><a name="book3"> Book 3 - Design</a></h2>
<p> Thus spake the master programmer:
</p>
<p> ``When the program is being tested, it is too late to make
design
changes.''
</p>
<h3 align="center">                                3.1</h3>
<p> There once was a man who went to a computer trade show.  Each day as
he entered, the man told the guard at the door:
</p>
<blockquote>
``I am a great thief, renowned for my feats of shoplifting. Be
forewarned, for this trade show shall not escape unplundered.''
</blockquote>
<p> This speech disturbed the guard greatly, because there were millions
of dollars of computer equipment inside, so he watched the man
carefully.  But the man merely wandered from booth to booth, humming
quietly to himself.
</p>
<p> When the man left, the guard took him aside and searched his
clothes,
but nothing was to be found.
</p>
<p> On the next day of the trade show, the man returned and chided
the guard
saying:  ``I escaped with a vast booty yesterday, but today will be even
better.'' So the guard watched him ever more closely, but to no avail.
</p>
<p> On the final day of the trade show, the guard could restrain his
curiosity
no longer.  ``Sir Thief,'' he said, ``I am so perplexed, I cannot live
in
peace.  Please enlighten me.  What is it that you are stealing?''
</p>
<p> The man smiled.  ``I am stealing ideas,'' he said.
</p>
<h3 align="center">                                 3.2</h3>
<p> There once was a master programmer who wrote unstructured programs.
A novice programmer, seeking to imitate him, also began to write
unstructured programs.  When the novice asked the master to evaluate
his progress, the master criticized him for writing unstructured
programs, saying, ``What is appropriate for the master is not
appropriate for the novice.  You must understand the Tao before
transcending structure.''
</p>
<h3 align="center">                                 3.3</h3>
<p> There was once a programmer who was attached to the court of the
warlord of Wu.  The warlord asked the programmer:  ``Which is easier to
design: an accounting package or an operating system?''
</p>
<p> ``An operating system,'' replied the programmer.
</p>
<p> The warlord uttered an exclamation of disbelief.  ``Surely an
accounting
package is trivial next to the complexity of an operating system,'' he
said.
</p>
<p> ``Not so,'' said the programmer, ``when designing an accounting
package,
the programmer operates as a mediator between people having different
ideas:  how it must operate, how its reports must appear, and how it
must conform to the tax laws.  By contrast, an operating system is not
limited by outside appearances.  When designing an operating system,
the programmer seeks the simplest harmony between machine and ideas.
This is why an operating system is easier to design.''
</p>
<p> The warlord of Wu nodded and smiled.  ``That is all good and
well, but
which is easier to debug?''
</p>
<p> The programmer made no reply.
</p>
<h3 align="center">                                 3.4</h3>
<p> A manager went to the master programmer and showed him the
requirements
document for a new application.  The manager asked the master:  ``How
long will it take to design this system if I assign five programmers to
it?''
</p>
<p> ``It will take one year,'' said the master promptly.
</p>
<p> ``But we need this system immediately or even sooner!  How long
will it
take if I assign ten programmers to it?''
</p>
<p> The master programmer frowned.  ``In that case, it will take two
years.''
</p>
<p> ``And what if I assign a hundred programmers to it?''
</p>
<p> The master programmer shrugged.  ``Then the design will never be
completed,'' he said.
</p>
<hr />
<h2 align="right"><a name="book4"> Book 4 - Coding</a></h2>
<p> Thus spake the master programmer:
</p>
<p> ``A well-written program is its own heaven; a poorly-written
program is its
own hell.''
</p>
<h3 align="center">                                 4.1</h3>
<p> A program should be light and agile, its subroutines connected like a
string of pearls.  The spirit and intent of the program should be
retained throughout.  There should be neither too little or too much,
neither needless loops nor useless variables, neither lack of
structure nor overwhelming rigidity.
</p>
<p> A program should follow the `Law of Least Astonishment'.  What
is this
law?  It is simply that the program should always respond to the user
in the way that astonishes him least.
</p>
<p> A program, no matter how complex, should act as a single unit.
The program
should be directed by the logic within rather than by outward
appearances.
</p>
<p> If the program fails in these requirements, it will be in a
state of
disorder and confusion.  The only way to correct this is to rewrite
the program.
</p>
<h3 align="center">                                 4.2</h3>
<p> A novice asked the master: ``I have a program that sometime runs and
sometimes aborts.  I have followed the rules of programming, yet I am
totally baffled.  What is the reason for this?''
</p>
<p> The master replied: ``You are confused because you do not
understand
Tao.  Only a fool expects rational behavior from his fellow humans.
Why do you expect it from a machine that humans have constructed?
Computers simulate determinism; only Tao is perfect.
</p>
<p> ``The rules of programming are transitory; only Tao is eternal.
Therefore you must contemplate Tao before you receive enlightenment.''
</p>
<p> ``But how will I know when I have received enlightenment?''
asked the
novice.
</p>
<p> ``Your program will then run correctly,'' replied the master.
</p>
<h3 align="center">                                 4.3</h3>
<p> A master was explaining the nature of Tao of to one of his novices.
``The Tao is embodied in all software - regardless of how
insignificant,'' said the master.
</p>
<p> ``Is the Tao in a hand-held calculator?'' asked the novice.
</p>
<p> ``It is,'' came the reply.
</p>
<p> ``Is the Tao in a video game?'' continued the novice.
</p>
<p> ``It is even in a video game,'' said the master.
</p>
<p> ``And is the Tao in the DOS for a personal computer?''
</p>
<p> The master coughed and shifted his position slightly. ``The
lesson
is over for today,'' he said.
</p>
<h3 align="center">                                 4.4</h3>
<p> Prince Wang's programmer was coding software.  His fingers danced
upon
the keyboard.  The program compiled without an error message, and the
program ran like a gentle wind.
</p>
<p> ``Excellent!'' the Prince exclaimed, ``Your technique is
faultless!''
</p>
<p> ``Technique?'' said the programmer turning from his terminal,
``What I
follow is Tao - beyond all techniques!  When I first began to program
I would see before me the whole problem in one mass.  After three
years I no longer saw this mass.  Instead, I used subroutines.  But now
I see nothing.  My whole being exists in a formless void.  My senses
are idle.  My spirit, free to work without plan, follows its own
instinct.  In short, my program writes itself.  True, sometimes there
are difficult problems.  I see them coming, I slow down, I watch
silently.  Then I change a single line of code and the difficulties
vanish like puffs of idle smoke.  I then compile the program.  I sit
still and let the joy of the work fill my being.  I close my eyes for
a moment and then log off.''
</p>
<p> Prince Wang said, ``Would that all of my programmers were as
wise!''
</p>
<hr />
<h2 align="right"> <a name="book5">Book 5 - Maintenance</a></h2>
<p> Thus spake the master programmer:
</p>
<p> ``Though a program be but three lines long, someday it will have
to be
maintained.''
</p>
<h3 align="center">                                 5.1</h3>
<p> A well-used door needs no oil on its hinges. <br />
A swift-flowing stream does not grow stagnant. <br />
Neither sound nor thoughts can travel through a vacuum. <br />
Software rots if not used. <br />
</p>
<p> These are great mysteries.
</p>
<h3 align="center">                                 5.2</h3>
<p> A manager asked a programmer how long it would take him to finish
the program on which he was working.  ``It will be finished tomorrow,''
the programmer promptly replied.
</p>
<p> ``I think you are being unrealistic,'' said the manager,
``Truthfully,
how long will it take?''
</p>
<p> The programmer thought for a moment. ``I have some features that
I
wish to add. This will take at least two weeks,'' he finally said.
</p>
<p> ``Even that is too much to expect,'' insisted the manager, ``I
will be
satisfied if you simply tell me when the program is complete.''
</p>
<p> The programmer agreed to this.
</p>
<p> Several years later, the manager retired.  On the way to his
retirement
luncheon, he discovered the programmer asleep at his terminal.  He had
been programming all night.
</p>
<h3 align="center">                                 5.3</h3>
<p> A novice programmer was once assigned to code a simple financial
package.
</p>
<p> The novice worked furiously for many days, but when his master
reviewed
his program, he discovered that it contained a screen editor, a set of
generalized graphics routines, an artificial intelligence interface,
but not the slightest mention of anything financial.
</p>
<p> When the master asked about this, the novice became indignant.
``Don't
be so impatient,'' he said, ``I'll put in the financial stuff
eventually.''
</p>
<h3 align="center">                                 5.4</h3>
<p> Does a good farmer neglect a crop he has planted? <br />
Does a good teacher overlook even the most humble student? <br />
Does a good father allow a single child to starve? <br />
Does a good programmer refuse to maintain his code? <br />
</p>
<hr />
<h2 align="right"> <a name="book6">Book 6 - Management</a></h2>
<p> Thus spake the master programmer:
</p>
<p> ``Let the programmers be many and the managers few - then all
will be
productive.''
</p>
<h3 align="center">                                 6.1</h3>
<p> When managers hold endless meetings, the programmers write games.
When accountants talk of quarterly profits, the development budget
is about to be cut.  When senior scientists talk blue sky, the clouds
are about to roll in.
</p>
<p> Truly, this is not the Tao of Programming.
</p>
<p> When managers make commitments, game programs are ignored. When
accountants make long-range plans, harmony and order are about to
be restored.  When senior scientists address the problems at hand,
the problems will soon be solved.
</p>
<p> Truly, this is the Tao of Programming.
</p>
<h3 align="center">                                 6.2</h3>
<p> Why are programmers non-productive? <br />
Because their time is wasted in meetings.
</p>
<p> Why are programmers rebellious? <br />
Because the management interferes too much.
</p>
<p> Why are the programmers resigning one by one? <br />
Because they are burnt out.
</p>
<p> Having worked for poor management, they no longer value their
jobs.
</p>
<h3 align="center">                                 6.3</h3>
<p> A manager was about to be fired, but a programmer who worked for him
invented a new program that became popular and sold well.  As a result,
the manager retained his job.
</p>
<p> The manager tried to give the programmer a bonus, but the
programmer
refused it, saying, ``I wrote the program because I thought it was
an interesting concept, and thus I expect no reward.''
</p>
<p> The manager upon hearing this remarked, ``This programmer,
though he
holds a position of small esteem, understands well the proper duty
of an employee.  Let us promote him to the exalted position of
management consultant!''
</p>
<p> But when told this, the programmer once more refused, saying,
``I exist so that I can program.  If I were promoted, I would do
nothing but waste everyone's time.  Can I go now?  I have a program
that I'm working on."
</p>
<h3 align="center">                                6.4</h3>
<p> A manager went to his programmers and told them:  ``As regards to
your work hours:  you are going to have to come in at nine in the
morning and leave at five in the afternoon.'' At this, all of them
became angry and several resigned on the spot.
</p>
<p> So the manager said:  ``All right, in that case you may set your
own
working hours, as long as you finish your projects on schedule.''
The programmers, now satisfied, began to come in at noon and work
to the wee hours of the morning.
</p>
<hr />
<h2 align="right"> <a name="book7">Book 7 - Corporate Wisdom</a></h2>
<p> Thus spake the master programmer:
</p>
<p> ``You can demonstrate a program for a corporate executive, but
you can't
make him computer literate.''
</p>
<h3 align="center">                                 7.1</h3>
<p> A novice asked the master:  ``In the east there is a great
tree-structure that men call `Corporate Headquarters'.  It is bloated
out of shape with vice presidents and accountants.  It issues a
multitude of memos, each saying `Go, Hence!' or `Go, Hither!' and
nobody knows what is meant.  Every year new names are put onto the
branches, but all to no avail.  How can such an unnatural entity be?"
</p>
<p> The master replied:  ``You perceive this immense structure and
are
disturbed that it has no rational purpose.  Can you not take
amusement from its endless gyrations?   Do you not enjoy the
untroubled ease of programming beneath its sheltering branches?
Why are you bothered by its uselessness?''
</p>
<h3 align="center">                                 7.2</h3>
<p> In the east there is a shark which is larger than all other fish.
It
changes into a bird whose wings are like clouds filling the sky.  When
this bird moves across the land, it brings a message from Corporate
Headquarters.  This message it drops into the midst of the programmers,
like a seagull making its mark upon the beach.  Then the bird mounts
on the wind and, with the blue sky at its back, returns home.
</p>
<p> The novice programmer stares in wonder at the bird, for he
understands
it not.  The average programmer dreads the coming of the bird, for he
fears its message.  The master programmer continues to work at his
terminal, for he does not know that the bird has come and gone.
</p>
<h3 align="center">                                 7.3</h3>
<p> The Magician of the Ivory Tower brought his latest invention for the
master programmer to examine.  The magician wheeled a large black box
into the master's office while the master waited in silence.
</p>
<p> ``This is an integrated, distributed, general-purpose
workstation,''
began the magician, ``ergonomically designed with a proprietary
operating system, sixth generation languages, and multiple state of
the art user interfaces.  It took my assistants several hundred man
years to construct.  Is it not amazing?''
</p>
<p> The master raised his eyebrows slightly.  ``It is indeed
amazing,'' he said.
</p>
<p> ``Corporate Headquarters has commanded,'' continued the
magician,
``that everyone use this workstation as a platform for new programs.
Do you agree to this?''
</p>
<p>
``Certainly,'' replied the master, ``I will have it transported to the
data center immediately!''  And the magician returned to his tower,
well pleased.
</p>
<p> Several days later, a novice wandered into the office of the
master
programmer and said, ``I cannot find the listing for my new program.
Do you know where it might be?''
</p>
<p> ``Yes,'' replied the master, ``the listings are stacked on the
platform
in the data center.''
</p>
<h3 align="center">                                 7.4</h3>
<p> The master programmer moves from program to program without fear.
No change in management can harm him. He will not be fired, even if
the project is cancelled.  Why is this?  He is filled with Tao.
</p>
<hr />
<h2 align="right"> <a name="book8">Book 8 - Hardware and Software</a></h2>
<p> Thus spake the master programmer:
</p>
<p> ``Without the wind, the grass does not move. Without software,
hardware is
useless.''
</p>
<h3 align="center">                                 8.1</h3>
<p> A novice asked the master:  ``I perceive that one computer company
is
much larger than all others.  It towers above its competition like a
giant among dwarfs.  Any one of its divisions could comprise an entire
business.  Why is this so?''
</p>
<p> The master replied, ``Why do you ask such foolish questions?
That
company is large because it is large.  If it only made hardware,
nobody would buy it.  If it only made software, nobody would use it.
If it only maintained systems, people would treat it like a servant.
But because it combines all of these things, people think it one of
the gods!  By not seeking to strive, it conquers without effort.''
</p>
<h3 align="center">                                 8.2</h3>
<p> A master programmer passed a novice programmer one day.  The master
noted the novice's preoccupation with a hand-held computer game.
``Excuse me,'' he said, ``may I examine it?''
</p>
<p> The novice bolted to attention and handed the device to the
master.
``I see that the device claims to have three levels of play: Easy,
Medium, and Hard,'' said the master.  ``Yet every such device has
another level of play, where the device seeks not to conquer the
human, nor to be conquered by the human.''
</p>
<p> ``Pray, great master,'' implored the novice, ``how does one find
this
mysterious setting?''
</p>
<p> The master dropped the device to the ground and crushed it
underfoot.
And suddenly the novice was enlightened.
</p>
<h3 align="center">                                 8.3</h3>
<p> There was once a programmer who worked upon microprocessors.  ``Look
at how well off I am here,'' he said to a mainframe programmer who came
to visit, ``I have my own operating system and file storage device.
I do not have to share my resources with anyone.  The software is self-
consistent and easy-to-use.  Why do you not quit your present job and
join me here?''
</p>
<p> The mainframe programmer then began to describe his system to
his friend,
saying ``The mainframe sits like an ancient sage meditating in the midst
of the data center.  Its disk drives lie end-to-end like a great ocean
of
machinery.  The software is as multifaceted as a diamond, and as
convoluted
as a primeval jungle.  The programs, each unique, move through the
system
like a swift-flowing river.  That is why I am happy where I am.''
</p>
<p> The microcomputer programmer, upon hearing this, fell silent.
But the
two programmers remained friends until the end of their days.
</p>
<h3 align="center">                                 8.4</h3>
<p> Hardware met Software on the road to Changtse.  Software said:
``You
are Yin and I am Yang.  If we travel together we will become famous
and earn vast sums of money.''  And so the set forth together, thinking
to conquer the world.
</p>
<p> Presently they met Firmware, who was dressed in tattered rags
and
hobbled along propped on a thorny stick.  Firmware said to them:
``The Tao lies beyond Yin and Yang. It is silent and still as a pool
of water.  It does not seek fame, therefore nobody knows its presence.
It does not seek fortune, for it is complete within itself.  It exists
beyond space and time.''
</p>
<p> Software and Hardware, ashamed, returned to their homes.
</p>
<hr />
<h2 align="right"> <a name="book9">Book 9 - Epilogue</a></h2>
<p> Thus spake the master programmer:
</p>
<p> ``It is time for you to leave.''
</p>
<hr />
<img src ="http://www.blogjava.net/sealyu/aggbug/320337.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2010-05-08 05:52 <a href="http://www.blogjava.net/sealyu/archive/2010/05/08/320337.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>领域驱动设计和开发实战(转)</title><link>http://www.blogjava.net/sealyu/archive/2010/03/29/316783.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Mon, 29 Mar 2010 00:55:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2010/03/29/316783.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/316783.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2010/03/29/316783.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/316783.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/316783.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 背景领域驱动设计（DDD）的中心内容是如何将业务领域概念映射到软件工件中。大部分关于此主题的著作和文章都以Eric Evans的书《领域驱动设计》为基础，主要从概念和设计的角度探讨领域建模和设计情况。这些著作讨论实体、值对象、服务等DDD的主要内容，或者谈论通用语言、界定的上下文（Bounded Context）和防护层（Anti-Corruption Layer）这些的概念。相关厂商...&nbsp;&nbsp;<a href='http://www.blogjava.net/sealyu/archive/2010/03/29/316783.html'>阅读全文</a><img src ="http://www.blogjava.net/sealyu/aggbug/316783.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2010-03-29 08:55 <a href="http://www.blogjava.net/sealyu/archive/2010/03/29/316783.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>正方形是否是长方形的子类?（转）</title><link>http://www.blogjava.net/sealyu/archive/2009/12/23/307045.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Wed, 23 Dec 2009 07:39:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2009/12/23/307045.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/307045.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2009/12/23/307045.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/307045.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/307045.html</trackback:ping><description><![CDATA[<p><strong><font size="3">长方形和正方形</font></strong></p>
<p><font size="3">正方形是否是长方形的子类的问题，西方一个很著名的思辨题。</font></p>
<p><font size="3">正确的写法是：</font></p>
<p><font size="3">长方形类：两个属性，宽度和高度；正方形类：一个属性，边。</font></p>
<p><font size="3">（LY注：这是至少流行了十年的思辨题目，最早来自于C++和Smalltalk领域。类似的这种思辨问题还有哪些呢？让我不禁对哲学又感冒起来了。查阅资料时意外找到了一个讨论区，里面有读者和作者关于此处的拓展讨论，真让人高兴。）</font></p>
<p><font size="3"><font size="+0"><font size="3">(LY注：书中没有提契约即Design by Contract的概念。子类应当完全继承父类的contract。</font><font size="3">《敏
捷软件开发：原则、模式与实践》一书中这样写，"基于契约设计(Design By
Constract),简称DBC"这项技术对LISKOV代换原则提供了支持.该项技术Bertrand
Meyer伯特兰做过详细的介绍:使用DBC,类的编写者显式地规定针对该类的契约.客户代码的编写者可以通过该契约获悉可以依赖的行为方式.契约是通过
每个方法声明的前置条件(preconditions)和后置条件(postconditions)来指定的.要使一个方法得以执行,前置条件必须为真.
执行完毕后,该方法要保证后置条件为真.就是说,在重新声明派生类中的例程(routine)时,只能使用相等或者更弱的前置条件来替换原始的前置条件,
只能使用相等或者更强的后置条件来替换原始的后置条件。&nbsp;
本书中长方形的Contract是width和height可以独立变化，这个contract在正方形中被破坏了。)</font></font></font></p>
<p><font size="3"><font size="+0"><font size="3">（LY注：注意，我们所有讨论的基础都应由类的行为决定。这使得长方形等类是动态的，而不是象现实生活中一样是静态的概念。）</font></font></font></p>
<p><font size="3"><font size="+0">&nbsp;</font></font></p>
<p><font size="3"><font size="+0"><strong><font size="3">正方形不可以作为长方形的子类</font></strong></font></font></p>
<p><font size="3"><font size="+0"><font size="3">如果设定一个<font size="+0">resize</font>方法，一直增加长方形的宽度，直到增加到宽度超过高度才可以。</font></font></font></p>
<p><font size="3"><font size="+0"><font size="3">那么如果针对子类正方形的对象调用<font size="+0">resize</font>方法，这个方法会导致正方形的边不断地增加下去，直到溢出为止。换言之，里氏法则被破坏掉了。</font></font></font></p>
<p><font size="3"><font size="+0"><u><font size="3">这个例子很重要，它意味着里氏代换与通常的数学法则和生活常识有不可混淆的区别。</font></u></font></font></p>
<p><font size="3"><font size="+0"><font size="3">(LY 注：常识认为，正方形is a 长方形，而且是一类特殊的长方形。但是在这里出了问题，如果我们系统中不会有这样的resize操作，是否正方形就可以作为长方形的子类了呢？看后文是可以的<font size="+0">)</font></font></font></font></p>
<p><font size="3"><font size="+0"><strong><font size="3">代码的重构</font></strong></font></font></p>
<p><font size="3"><font size="+0"><font size="3">长方形和正方形到底应该是什么关系呢？</font></font></font></p>
<p><font size="3"><font size="+0"><font size="3">它们应该都是四边形类的子类。四边形类中没有赋值方法，因类似上文的<font size="+0">resize()</font>方法不可能适用于四边形类，而只能只用于不同的具体子类长方形和正方形。因此里氏代换原则不会被破坏。（LY注：针对需要赋值操作的情况）</font></font></font></p>
<p><font size="3"><font size="+0"><strong><font size="3">从抽象类继承</font></strong></font></font></p>
<p><font size="3"><font size="+0"><u><font size="3">应尽量从抽象类继承，而不是从具体类继承。</font></u></font></font></p>
<p><font size="3"><font size="+0"><font size="3">上文对长方形和正方形的重构使用了重构的第一种方法。增加一个抽象类，让两个具体类都成为抽象类的子类。</font></font></font></p>
<p><font size="3"><font size="+0"><font size="3"><u>记住一条指导性的原则，如果有一个由继承关系形成的等级结构的话，在等级结构树图上的所有树叶节点都应当是具体类；而所有的树枝节点都应当是抽象类或者<font size="+0">Java</font></u><u>接口。</u></font></font></font></p>
<p><font size="3"><font size="+0"><font size="3">问答题</font></font></font></p>
<p><font size="3"><font size="+0"><font size="+0"><font size="3">1、</font>&nbsp; </font><font size="3">一个有名的思辨题，<font size="+0">filename</font>能不能作为<font size="+0">string</font>类的子类？</font></font></font></p>
<p><font size="3"><font size="+0"><font size="3">答：不能。<font size="+0">Filename</font>对象不能实现<font size="+0">string</font>对象的所有行为。比如两个<font size="+0">string</font>对象相加可以给出一个新的有效的<font size="+0">string</font>对象。而两个<font size="+0">filename</font>对象相加未必会得到一个新的有效的<font size="+0">Filename</font>对象。</font></font></font></p>
<p><font size="3"><font size="+0"><font size="3"><font size="+0">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font>另外，<font size="+0">Java</font>中的<font size="+0">String</font>类型是<font size="+0">final</font>类型，因此不可以继承。</font></font></font></p>
<p><font size="3"><font size="+0"><font size="+0"><font size="3">2、</font>&nbsp; </font><font size="3">如果正方形的边长不会发生改变，是否可以成为长方形的子类呢？（LY注：不变正方形，就是边长不会发生变化的正方形，也就是遵守不变模式的正方形。不变(Immutable)模式,一个对象在对象在创建之后就不再变化。）</font></font></font></p>
<p><font size="3"><font size="+0"><font size="3">答：可以。实现时，父类有两个属性宽度和高度。子类有三个属性宽度、高度和边。针对每一个属性，包含一个内部变量，一个<font size="+0">Set</font>值方法，一个<font size="+0">Get</font>值方法。子类正方形只需要将<font size="+0">Set</font>值方法不写任何语句即可。</font></font></font></p>
<p><font size="3"><font size="+0"><font size="+0"><font size="3">3、</font>&nbsp; </font><font size="3">从里式代换角度看<font size="+0">Java</font>中<font size="+0">Properties</font>和<font size="+0">Hashtable</font>的关系是否合适？</font></font></font></p>
<p><font size="3"><font size="+0"><font size="3">答：不合适。在<font size="+0">Java</font>中，<font size="+0">Properties</font>是<font size="+0">Hashtable</font>的子类。显然<font size="+0">Properties</font>是一种特殊的<font size="+0">Hashtable,</font>它只接受<font size="+0">string</font>类型的键（<font size="+0">Key</font>）和值（<font size="+0">Value</font>）。但是，父类<font size="+0">Hashtable</font>可以接受任何类型的键和值。这意味着，在一些需要非<font size="+0">String</font>类型的键和值的地方，<font size="+0">Properties</font>不能取代<font size="+0">Hashtable</font>。</font></font></font></p>
<p><font size="3"><font size="+0"><font size="3">（LY注：合成/聚合复用原则中有更详细的讨论，应使用合成/组合而不是继承。它们是has a的关系而不是is a的关系。）</font></font></font></p>
<p><br />
</p>
<p><br />
</p>
<p><br />
</p>
<font size="3">另外，有一篇有意思的文章不赞同这个观点：<br />
</font>
<p>本文假定读者已经了解有关正方形不是长方形的相关内容。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;之前人们讨论的正方形长方形的问题的关键
在哪里？我觉得就在于改动长方形的边的长度。我们可以这么考虑一下，一个长方形的instance的边长应该是可变的吗？我觉得一旦一个长方形的边长改变
之后它就成了另一个长方形了（一个新的instance）。所以长方形类里面不应该有改变其边长的方法，一个长方形实例各个的边长应当在new它的时候确
定下来，并且它们应当是immutable的。基于这种考虑，我设计的长方形和正方形的类如下所示：<br />
//长方形<br />
public class Rectangle {<br />
&nbsp; private final int width;<br />
&nbsp; private final int height;<br />
&nbsp; <br />
&nbsp; public Rectangle(int width, int height) {<br />
&nbsp;&nbsp;&nbsp; this.width = width;<br />
&nbsp;&nbsp;&nbsp; this.height = height;<br />
&nbsp; }<br />
&nbsp; <br />
&nbsp; public int getWidth() {<br />
&nbsp;&nbsp;&nbsp; return width;<br />
&nbsp; }</p>
<p>&nbsp; public int getHeight() {<br />
&nbsp;&nbsp;&nbsp; return height;<br />
&nbsp; }<br />
&nbsp; <br />
&nbsp; public int getArea() {<br />
&nbsp;&nbsp;&nbsp; return width*height;<br />
&nbsp; }<br />
}</p>
<p>//正方形<br />
public class Square extends Rectangle{<br />
&nbsp; private final int side;<br />
&nbsp; <br />
&nbsp; public Square(int side) {<br />
&nbsp;&nbsp;&nbsp; super(side, side);<br />
&nbsp;&nbsp;&nbsp; this.side = side;<br />
&nbsp; }<br />
&nbsp; <br />
&nbsp; public int getSide() {<br />
&nbsp;&nbsp;&nbsp; return side;<br />
&nbsp; }<br />
}</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
这种继承关系就既符合现实中的父子关系也遵循LSP。之所以这么设计，我的想法是一个类所具有的方法不应当能够改变其本质。比如有一个Men类，它可以有
eat(),sleep(),work(),makeLovewith(Person
p)方法，但是如果你在里面定义denatureToWomen(),denatureToEunuch()就很不恰当了，因为这改变了其本质，导致这个
Men的实例不再属于Men类（至少已经和现实不吻合）了。除非这两个方法不能改变该实例本质，否则在Men里面定义这两个方法本身就是有问题的。不过如
果用下面这种方式定义也许可行：<br />
public Women denatureToWomen() {<br />
&nbsp; Women w = new Women();<br />
&nbsp; //set attributes here<br />
&nbsp; return w;<br />
}</p>
<p>public Eunuch denatureToEunuch() {<br />
&nbsp; Eunuch e = new Eunuch();<br />
&nbsp; //set attributes here<br />
&nbsp; return e;<br />
}</p>
<p>这样一来，调用denatureToWomen()会产生一个新的实例，原来的那个Men实例依然存在，这和现实生活依然不吻合，现实生活中一个实例不光可以上型(upcast)，还可以平行型，寒。。。</p>
<p>总之一句话，一个类的方法不应该改变其实例的本质。</p>
<br />
<img src ="http://www.blogjava.net/sealyu/aggbug/307045.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2009-12-23 15:39 <a href="http://www.blogjava.net/sealyu/archive/2009/12/23/307045.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>设计模式原则详解（转）</title><link>http://www.blogjava.net/sealyu/archive/2009/12/23/307043.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Wed, 23 Dec 2009 07:34:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2009/12/23/307043.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/307043.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2009/12/23/307043.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/307043.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/307043.html</trackback:ping><description><![CDATA[<p>这篇文章,不需要你一次就看懂,如果你真的能一次都看懂,我想设计模式对于你来说
已经没什么难度了..因为设计模式就是要体现这些原则的,你可以把设计原则看做是一门语言,设计模式是由这些语言编码的程序..你既然已经明白,精通了语
言,剩下的编码自然是很简单的事情,编码的越多则经验越多,经验越多则对原则的理解就越深...这是一个<span style="color: red;">学习领悟</span>的过程..</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 我希望这篇文章能帮助新人感受到<span style="color: red;">设计模式的乐趣</span>,避免重复编
码....减少劳动量..如果你能在用心静静的体会文章的每个字,每段话的意思,这样可以避免走很多弯路...我以前学习设计模式的时候,就是因为忽略了
原则,凭着感觉,看着书来学习设计模式,结果就是知其然而不知其所以然....如果你是初学设计模式,再了解了OOP的三大原则(封套,继承,多态)之
后,请反复的结合原则,来学习设计模式..这样可以达到事半功倍的效果...<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: red;">设计模式的核心原则是:"开-闭"原则(&nbsp; Open - Closed Principle 缩写:OCP&nbsp; ),一切的一切都是围绕着"开-闭"原则展开的..<br />
</span>&nbsp;&nbsp;&nbsp;&nbsp; 意思是,在一个系统中,对于扩展是开放的,对于修改是关闭的,一个好的系统是在不修改源代码的情况下,可以扩展你的功能..而实现开闭原则的关键就是抽象化.<br />
&nbsp;&nbsp;&nbsp;&nbsp; 在"开-闭"原则中,不允许修改的是抽象的类或者接口,允许扩展的是具体的实现类,抽象类和接口在"开-闭"原则中扮演着极其重要的角色..即要预知可能变化的需求.又预见所有可能已知的扩展..所以在这里"抽象化"是关键!!!</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: red;">可变性的封闭原则:找到系统的可变因素,将它封装起来..</span>这是
对"开-闭"原则最好的实现..不要把你的可变因素放在多个类中,或者散落在程序的各个角落..你应该将可变的因素,封套起来..并且切忌不要把所用的可
变因素封套在一起..最好的解决办法是,分块封套你的可变因素!!避免超大类,超长类,超长方法的出现!!给你的程序增加艺术气息,将程序艺术化是我们的
目标!!</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: red;">里氏代换原则:任何基类可以出现的地方,子类也可以出现..</span>如果你通读过&lt;Java编程思想&gt;,我想你应该明白这个原则,在书中,Bruce Eckel 大师用了大量的章节来讲解"向上转型"和"向下转型",我想目的很清楚,不仅是要你明白类的型别,更重要的是要你明白父类与子类的关系..<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: red;">依赖倒转原则:要依赖抽象,而不要依赖具体的实现..</span>如
果说开闭原则是目标,依赖倒转原则是到达"开闭"原则的手段..如果要达到最好的"开闭"原则,就要尽量的遵守依赖倒转原则..可以说依赖倒转原则是对"
抽象化"的最好规范!!我个人感觉,依赖倒转原则也是里氏代换原则的补充..你理解了里氏代换原则,再来理解依赖倒转原则应该是很容易的..<br />
&nbsp; 合成/聚合原则:要尽量使用合成/聚合原则,而不是继承关系达到软件复用的目的..此原则和里氏代换原则氏相辅相成的,两者都是具体实现"开-闭"原则的规范..违反这一原则:就无法实现"开-闭"原则..先来看看什么是合成,什么是聚合.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: red;">什么是合成?<br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;
合成:是指一个整体对依托他而存在的关系,例如:一个人对他的房子和家具,其中他的房子和家具是不能被共享的,因为那些东西都是他自己的..并且人没了,
这个也关系就没了..这个例子就好像,乌鸡百凤丸这个产品,它是有乌鸡和上等药材合成而来的一样..也比如网络游戏中的武器装备合成一样,多种东西合并为
一种超强的东西一样..<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: red;">什么是聚合?<br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
聚合:聚合是比合成关系的一种更强的依赖关系,聚合是一个整体对个体的部分,例如,一个奔驰S360汽车,对奔驰S360引擎,奔驰S360轮胎的关
系..这些关系就是带有聚合性质的..因为奔驰S360引擎和奔驰S360轮胎他们只能被奔驰S360汽车所用,离开了奔驰S360汽车,它们就失去了存
在的意义..在我们的设计中,这样的关系不应该频繁出现..这样会增大设计的耦合度..<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 明白了合成和聚合关系,再来理解合成/聚合原则应该就清楚了..要避免在系统设计中出现,一个类的继承层次超过3次..如果这样的话,可以考虑重构你的代码,或者重新设计结构..当然最好的办法就是考虑使用合成/聚合原则...<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: red;">迪米特法则:系统中的类,尽量不要与其他类互相作用,减少类之间的耦合度</span>,
因为在你的系统中,扩展的时候,你可能需要修改这些类,而类与类之间的关系,决定了修改的复杂度,相互作用越多,则修改难度就越大,反之,如果相互作用的
越小,则修改起来的难度就越小..例如A类依赖B类,则B类依赖C类,当你在修改A类的时候,你要考虑B类是否会受到影响,而B类的影响是否又会影响到C
类..如果此时C类再依赖D类的话,呵呵,我想这样的修改有的受了..<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: red;">接口隔离法则:这个法则与迪米特法则是相通的,</span>迪米特法则是目的,而接口隔离法则是对迪米特法则的规范..为了做到尽可能小的耦合性,我们需要使用接口来规范类,用接口来约束类.要达到迪米特法则的要求,最好就是实现接口隔离法则,实现接口隔离法则,你也就满足了迪米特法则...<br />
&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果你能看这里,说明你已经对这些原则了有了感性的认识..这些原则是设计模式的核心,如果不能充分理解这些原则,是很难理解好设计模式的..</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果第一遍看不懂,没关系,请<span style="color: red;"><strong>反复揣摩,细读每个字,每句话..</strong></span>对于这些原则,我也是看了N*N遍才明白的(这期间也没任何人指点过我,更每人讲的这么细,奖励自己一下先<img src="http://www.cnblogs.com/Emoticons/QQ/05.gif" width="20" border="0" height="20"  alt="" />,
汗啊)..我推荐看完原则之后,请看设计模式,看两三个模式,然后理解一下,自己动手把模式实现出来,再回头来看原则,你会感觉,你的模式一定是满足了其
中的某些原则!!这是必然的!!只要你理解了原则,设计模式不难理解..就好比,有了内功基础的你,再来学习刀,剑,枪这些武器的时候,要比那些直接学习
刀,枪,剑的人,快很多,效果也好很多...<br />
<br />
&nbsp;&nbsp;&nbsp; 如果你要了解设计模式,在园子里也有N多好的设计模式文章,精华区的设计模式区..有很多都是不错的介绍文章..另外近段时间,我也在发表关于设计模式的心得..如果有兴趣,可以关注一下...<img src="http://www.cnblogs.com/Emoticons/QQ/14.gif" width="20" border="0" height="20"  alt="" /></p>
<img src ="http://www.blogjava.net/sealyu/aggbug/307043.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2009-12-23 15:34 <a href="http://www.blogjava.net/sealyu/archive/2009/12/23/307043.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>设计模式--笔记--职责链模式</title><link>http://www.blogjava.net/sealyu/archive/2009/12/23/307041.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Wed, 23 Dec 2009 07:09:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2009/12/23/307041.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/307041.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2009/12/23/307041.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/307041.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/307041.html</trackback:ping><description><![CDATA[<h2>一、&nbsp;职责链（Chain of Responsibility）模式</h2>
<p>责任链模式是一种对象的行为模式【GOF95】。在责任链模式里，很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递，
直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求，这使得系统可以在不影响客户端的情况下动态地重新
组织链和分配责任。</p>
<p><strong>从击鼓传花谈起</strong></p>
<p>击鼓传花是一种热闹而又紧张的饮酒游戏。在酒宴上宾客依次坐定位置，由一人击鼓，击鼓的地方与传花的地方是分开的，以示公正。开始击鼓时，花束就开始依次传递，鼓声一落，如果花束在某人手中，则该人就得饮酒。</p>
<p>击鼓传花便是责任链模式的应用。责任链可能是一条直线、一个环链或者一个树结构的一部分。</p>
<h2><br />
二、&nbsp;责任链模式的结构</h2>
<p>责任链模式涉及到的角色如下所示：</p>
<p><img src="http://zhenyulu.cnblogs.com/images/cnblogs_com/zhenyulu/Pic98.gif" width="319" border="0" height="190"  alt="" /></p>
<p><font color="#000080">抽象处理者(Handler)角色：</font>定义出一个处理请求的接口。如果需要，接口可以定义出一个方法，以设定和返回对下家的引用。这个角色通常由一个抽象类或接口实现。</p>
<p><font color="#000080">具体处理者(ConcreteHandler)角色：</font>具体处理者接到请求后，可以选择将请求处理掉，或者将请求传给下家。由于具体处理者持有对下家的引用，因此，如果需要，具体处理者可以访问下家。</p>
<h2><br />
</h2>
<br />
<div id="tf_edit_html_title" class="tf_edit_html_title">Chain of Responsibility模式的实现</div>
要实现Chain of Responsibility模式，需要满足该模式的基本要件：<br />
1，对象链的组织。需要将某任务的所有职责执行对象以链的形式加以组织。<br />
2，消息或请求的传递。将消息或请求沿着对象链传递，以让处于对象链中的对象得到处理机会。<br />
3，处于对象链中的对象的职责分配。不同的对象完成不同的职责。<br />
4，任务的完成。处于对象链的末尾的对象结束任务并停止消息或请求的继续传递。<br />
<br />
<div id="tf_edit_html_title" class="tf_edit_html_title">Chain of Responsibility模式的优优缺点：</div>
<strong>优点：</strong><br />
1，责任的分担。每个类只需要处理自己该处理的工作（不该处理的传递给下一个对象完成），明确各类的责任范围，符合类的最小封装原则。<br />
2，可以根据需要自由组合工作流程。如工作流程发生变化，可以通过重新分配对象链便可适应新的工作流程。<br />
3，类与类之间可以以松耦合的形式加以组织。<br />
<strong>缺点：</strong><br />
因为处理时以链的形式在对象间传递消息，根据实现方式不同，有可能会影响处理的速度。<br />
<br />
<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: #0000ff;">namespace</span><span style="color: #000000;">&nbsp;ChainOfResponsibility_DesignPattern<br />
{<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">using</span><span style="color: #000000;">&nbsp;System;<br />
<br />
&nbsp;&nbsp;&nbsp;&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;Handler&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">protected</span><span style="color: #000000;">&nbsp;Handler&nbsp;successorHandler;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">abstract</span><span style="color: #000000;">&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;HandleRequest(Request&nbsp;request);&nbsp;&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;SetSuccessor(Handler&nbsp;sucessor)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;successorHandler&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;sucessor;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;ConcreteHandler1&nbsp;:&nbsp;Handler<br />
&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">override</span><span style="color: #000000;">&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;HandleRequest(Request&nbsp;request)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;determine&nbsp;if&nbsp;we&nbsp;can&nbsp;handle&nbsp;the&nbsp;request</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;(request.RequestType&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">1</span><span style="color: #000000;">)&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;some&nbsp;complex&nbsp;decision&nbsp;making!</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<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;">&nbsp;request&nbsp;handling&nbsp;code&nbsp;goes&nbsp;here</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;Console.WriteLine(</span><span style="color: #000000;">"</span><span style="color: #000000;">request&nbsp;handled&nbsp;in&nbsp;ConcreteHandler1</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">else</span><span style="color: #000000;">&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<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;">&nbsp;not&nbsp;handled&nbsp;here&nbsp;-&nbsp;pass&nbsp;on&nbsp;to&nbsp;next&nbsp;in&nbsp;the&nbsp;chain</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;">if</span><span style="color: #000000;">&nbsp;(successorHandler&nbsp;</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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;successorHandler.HandleRequest(request);<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;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;ConcreteHandler2&nbsp;:&nbsp;Handler<br />
&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">override</span><span style="color: #000000;">&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;HandleRequest(Request&nbsp;request)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;determine&nbsp;if&nbsp;we&nbsp;can&nbsp;handle&nbsp;the&nbsp;request</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;(request.RequestType&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">2</span><span style="color: #000000;">)&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;some&nbsp;complex&nbsp;decision&nbsp;making!</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<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;">&nbsp;request&nbsp;handling&nbsp;code&nbsp;goes&nbsp;here</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;Console.WriteLine(</span><span style="color: #000000;">"</span><span style="color: #000000;">request&nbsp;handled&nbsp;in&nbsp;ConcreteHandler2</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">else</span><span style="color: #000000;">&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<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;">&nbsp;not&nbsp;handled&nbsp;here&nbsp;-&nbsp;pass&nbsp;on&nbsp;to&nbsp;next&nbsp;in&nbsp;the&nbsp;chain</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;">if</span><span style="color: #000000;">&nbsp;(successorHandler&nbsp;</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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;successorHandler.HandleRequest(request);<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;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;ConcreteHandler3&nbsp;:&nbsp;Handler<br />
&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">override</span><span style="color: #000000;">&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;HandleRequest(Request&nbsp;request)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;determine&nbsp;if&nbsp;we&nbsp;can&nbsp;handle&nbsp;the&nbsp;request</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;(request.RequestType&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">3</span><span style="color: #000000;">)&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;some&nbsp;complex&nbsp;decision&nbsp;making!</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<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;">&nbsp;request&nbsp;handling&nbsp;code&nbsp;goes&nbsp;here</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;Console.WriteLine(</span><span style="color: #000000;">"</span><span style="color: #000000;">request&nbsp;handled&nbsp;in&nbsp;ConcreteHandler3</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">else</span><span style="color: #000000;">&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<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;">&nbsp;not&nbsp;handled&nbsp;here&nbsp;-&nbsp;pass&nbsp;on&nbsp;to&nbsp;next&nbsp;in&nbsp;the&nbsp;chain</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;">if</span><span style="color: #000000;">&nbsp;(successorHandler&nbsp;</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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;successorHandler.HandleRequest(request);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&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;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;Request&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&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;iRequestType;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">string</span><span style="color: #000000;">&nbsp;strRequestParameters;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;Request(</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;requestType,&nbsp;</span><span style="color: #0000ff;">string</span><span style="color: #000000;">&nbsp;requestParameters)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;iRequestType&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;requestType;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;strRequestParameters&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;requestParameters;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<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;">int</span><span style="color: #000000;">&nbsp;RequestType&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">get</span><span style="color: #000000;">&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;iRequestType;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">set</span><span style="color: #000000;">&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;iRequestType&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;value;<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;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #808080;">///</span><span style="color: #008000;">&nbsp;</span><span style="color: #808080;">&lt;summary&gt;</span><span style="color: #008000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #808080;">///</span><span style="color: #008000;">&nbsp;&nbsp;&nbsp;&nbsp;Summary&nbsp;description&nbsp;for&nbsp;Client.<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #808080;">///</span><span style="color: #008000;">&nbsp;</span><span style="color: #808080;">&lt;/summary&gt;</span><span style="color: #808080;"><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;">class</span><span style="color: #000000;">&nbsp;Client<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;">static</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;Main(</span><span style="color: #0000ff;">string</span><span style="color: #000000;">[]&nbsp;args)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;Set&nbsp;up&nbsp;chain&nbsp;(usually&nbsp;one&nbsp;need&nbsp;to&nbsp;be&nbsp;done&nbsp;once)</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Handler&nbsp;firstHandler&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;ConcreteHandler1();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Handler&nbsp;secondHandler&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;ConcreteHandler2();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Handler&nbsp;thirdHandler&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;ConcreteHandler3();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;firstHandler.SetSuccessor(secondHandler);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;secondHandler.SetSuccessor(thirdHandler);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;After&nbsp;setting&nbsp;up&nbsp;the&nbsp;chain&nbsp;of&nbsp;responsibility,&nbsp;we&nbsp;can<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;now&nbsp;generate&nbsp;requests&nbsp;and&nbsp;pass&nbsp;then&nbsp;off&nbsp;to&nbsp;the&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;chain&nbsp;to&nbsp;be&nbsp;handled<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;generate&nbsp;and&nbsp;fire&nbsp;request</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Request&nbsp;newRequest&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Request(</span><span style="color: #000000;">2</span><span style="color: #000000;">,</span><span style="color: #000000;">"</span><span style="color: #000000;">This&nbsp;are&nbsp;the&nbsp;request&nbsp;parameters</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;firstHandler.HandleRequest(newRequest);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;</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;}<br />
}<br />
</span></div>
<br />
<br />
<img src ="http://www.blogjava.net/sealyu/aggbug/307041.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2009-12-23 15:09 <a href="http://www.blogjava.net/sealyu/archive/2009/12/23/307041.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>设计模式--笔记--装饰模式</title><link>http://www.blogjava.net/sealyu/archive/2009/12/23/307034.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Wed, 23 Dec 2009 06:15:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2009/12/23/307034.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/307034.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2009/12/23/307034.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/307034.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/307034.html</trackback:ping><description><![CDATA[1.装饰模式（Decorator）：<br />
<font size="3"><span style="font-family: 宋体;">装饰模式（</span><font face="Times New Roman">Decorator</font><span style="font-family: 宋体;">）也叫包装器模式（</span><font face="Times New Roman">Wrapper</font><span style="font-family: 宋体;">）。</span><font face="Times New Roman">GOF</font><span style="font-family: 宋体;">在《设计模式》一书中给出的定义为：动态地给一个对象添加一些额外的职责。就增加功能来说，</span><font face="Times New Roman">Decorator</font><span style="font-family: 宋体;">模式相比生成子类更为灵活。<br />
</span></font><font size="3"><span style="font-family: 宋体;">装饰模式的组成：</span></font>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt;"><font size="3"><font face="Times New Roman">1)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><span style="font-family: 宋体;">抽象构件角色（</span><font face="Times New Roman">Component</font><span style="font-family: 宋体;">）：定义一个抽象接口，以规范准备接收附加责任的对象。</span></font></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt;"><font size="3"><font face="Times New Roman">2)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><span style="font-family: 宋体;">具体构件角色</span><font face="Times New Roman">(Concrete Component)</font><span style="font-family: 宋体;">：这是被装饰者，定义一个将要被装饰增加功能的类。</span></font></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt;"><font size="3"><font face="Times New Roman">3)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><span style="font-family: 宋体;">装饰角色</span><font face="Times New Roman">(Decorator)</font><span style="font-family: 宋体;">：持有一个构件对象的实例，并定义了抽象构件定义的接口。</span></font></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt;"><font size="3"><font face="Times New Roman">4)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><span style="font-family: 宋体;">具体装饰角色</span><font face="Times New Roman">(Concrete Decorator)</font><span style="font-family: 宋体;">：负责给构件添加增加的功能。</span></font></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt;"><font size="3"><span style="color: black; font-family: Tahoma;">JAVA</span><span style="color: black; font-family: 宋体;">中</span><span style="color: black; font-family: Tahoma;">IO</span><span style="color: black; font-family: 宋体;">流的设计就大量运用了装饰模式。看看我们熟悉的代码</span><span style="color: black; font-family: Tahoma;">:</span></font></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt;"><font size="3"><span style="color: black; font-family: Tahoma;"><br />
</span></font></p>
装饰模式的特点；<br />
（1） 装饰对象和真实对象有相同的接口。这样客户端对象就可以以和真实对象相同的方式和装饰对象交互。<br />
（2） 装饰对象包含一个真实对象的索引（reference）<br />
（3） 装饰对象接受所有的来自客户端的请求。它把这些请求转发给真实的对象。<br />
（4） 装饰对象可以在转发这些请求以前或以后增加一些附加功能。这样就确保了在运行时，不用修改给定对象的结构就可以在外部增加附加的功能。在面向对象的设计中，通常是通过继续来实现对给定类的功能扩展。<br />
<br />
<br />
<p style="margin: 0cm 0cm 0pt;"><span style="color: #99cc00;"><font size="3"><font face="Times New Roman">BufferedReader br = <strong>new</strong> BufferedReader(<strong>new</strong> InputStreamReader(<strong>new</strong> FileInputStream("..")));</font></font></span></p>
<p style="margin: 0cm 0cm 0pt;"><span style="font-family: 宋体;"><font size="3">层层包装，增强功能。这就是装饰模式的要旨。</font></span></p>
<p style="margin: 0cm 0cm 0pt;"><img src="file:///C:/DOCUME%7E1/yuhaibo/LOCALS%7E1/Temp/moz-screenshot-5.png" alt="" /><span style="font-family: 宋体;"><img alt="" src="http://images.cnblogs.com/cnblogs_com/kid-li/Decorator1.jpg" width="383" border="0" height="322" /><br />
</span></p>
<img src ="http://www.blogjava.net/sealyu/aggbug/307034.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2009-12-23 14:15 <a href="http://www.blogjava.net/sealyu/archive/2009/12/23/307034.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>设计模式：简单工厂、工厂方法、抽象工厂之小结与区别 (转)</title><link>http://www.blogjava.net/sealyu/archive/2009/12/10/305454.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Thu, 10 Dec 2009 09:01:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2009/12/10/305454.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/305454.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2009/12/10/305454.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/305454.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/305454.html</trackback:ping><description><![CDATA[<p>简单工厂，工厂方法，抽象工厂都属于设计模式中的创建型模式。其主要功能都是帮助我们把对象的实例化部分抽取了出来，优化了系统的架构，并且增强了系统的扩展性。</p>
<p>本文是本人对这三种模式学习后的一个小结以及对他们之间的区别的理解。</p>
<p><strong>简单工厂</strong>
</p>
<p>简单工厂模式的工厂类一般是使用静态方法，通过接收的参数的不同来返回不同的对象实例。</p>
<p>不修改代码的话，是无法扩展的。</p>
<p><img src="http://p.blog.csdn.net/images/p_blog_csdn_net/superbeck/EntryImages/20090814/SimpleFactory.jpg" alt="简单工厂" />
</p>
<p><strong>工厂方法</strong>
</p>
<p>工厂方法是针对每一种产品提供一个工厂类。通过不同的工厂实例来创建不同的产品实例。</p>
<p>在同一等级结构中，支持增加任意产品。</p>
<p><img src="http://p.blog.csdn.net/images/p_blog_csdn_net/superbeck/EntryImages/20090814/FactoryMethod.jpg" alt="工厂方法" />
</p>
<p><strong>抽象工厂</strong>
</p>
<p>抽象工厂是应对产品族概念的。比如说，每个汽车公司可能要同时生产轿车，货车，客车，那么每一个工厂都要有创建轿车，货车和客车的方法。</p>
<p>应对产品族概念而生，增加新的产品线很容易，但是无法增加新的产品。</p>
<p><img src="http://p.blog.csdn.net/images/p_blog_csdn_net/superbeck/EntryImages/20090814/AbstractFactory.jpg" alt="抽象工厂" />
</p>
<p><strong>小结</strong>
</p>
<p>★工厂模式中，重要的是工厂类，而不是产品类。产品类可以是多种形式，多层继承或者是单个类都是可以的。但要明确的，工厂模式的接口只会返回一种类型的实例，这是在设计产品类的时候需要注意的，最好是有父类或者共同实现的接口。</p>
<p>★使用工厂模式，返回的实例一定是工厂创建的，而不是从其他对象中获取的。</p>
<p>★工厂模式返回的实例可以不是新创建的，返回由工厂创建好的实例也是可以的。</p>
<p><strong>区别</strong>
</p>
<p><span style="background-color: #ffffff;">简单工厂</span>
<span style="background-color: #ffffff;">：</span>
用来生产同一等级结构中的任意产品。（对于增加新的产品，无能为力）</p>
<p><span style="background-color: #ffffff;"> 工厂模式 ：用来生产同一等级结构中的固定产品。（支持增加任意产品） &nbsp; <br />
抽象工厂
：用来生产不同产品族的全部产品。（对于增加新的产品，无能为力；支持增加产品族） &nbsp; </span>
</p>
<p>以上三种<span style="background-color: #ffffff;">工厂</span>
方法在等级结构和产品族这两个方向上的支持程度不同。所以要根据情况考虑应该使用哪种方法。 &nbsp;
</p>
<img src ="http://www.blogjava.net/sealyu/aggbug/305454.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2009-12-10 17:01 <a href="http://www.blogjava.net/sealyu/archive/2009/12/10/305454.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>UML中的几种关系</title><link>http://www.blogjava.net/sealyu/archive/2009/12/10/305420.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Thu, 10 Dec 2009 06:47:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2009/12/10/305420.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/305420.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2009/12/10/305420.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/305420.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/305420.html</trackback:ping><description><![CDATA[UML中的各种关系： <br />
&nbsp;&nbsp;&nbsp;&nbsp; (1)依赖(dependency)是两个事物间的语义关系，其中一个事物(独立事物)发生变化会影响另一个事物(依赖事物)的语义。在图形上，把一个依赖画成一条可能有方向的虚线。 <br />
&nbsp;&nbsp;&nbsp;&nbsp;
(2)关联(association)是一种结构关系，连接模型元素及链接实例，用一条实线来表示。 <br />
&nbsp;&nbsp;&nbsp;&nbsp;
(3)泛化(generalization)是一种特殊／一般关系，特殊元素(子元素)的对象可替代一般元素(父元素)的对象，用这种方法，子元素共享了
父元素的结构和行为。在图形上，把一个泛化关系画成一条带有空心箭头的实线，它指向父元素。 <br />
&nbsp;&nbsp;&nbsp;&nbsp;
(4)实现(realization)是类元之间的语义关系，其中一个类元指定了由另一个类元保证执行的契约。在两种地方要遇到实现关系：一种是在接口和
实现它们的类或构件之间；另一种是在用例和实现它们的协作之间。在图形上，把一个实现关系画成一条带有空心箭头的虚线。&nbsp; <br />
&nbsp;
&nbsp;&nbsp; (5)聚集(aggregation)表示整体与部分的关系，用一条实线加空心菱形来表示；<br />
&nbsp;&nbsp;&nbsp;&nbsp;
(6)组成(Composition)表示整体与部分的有一关系，用一条实线加实心菱形来表示；
<p><br />
</p>
<p>UML中几种类间关系：继承、实现、依赖、关联、聚合、组合的联系与区别</p>
<h3>继承</h3>
<p>指的是一个类（称为子类、子接口）继承另外的一个类（称为父类、父接口）的功能，并可以增加它自己的新功能的能力，继承是类与类或者接口与接口之间最常见的关系；在Java中此类关系通过关键字extends明确标识，在设计时一般没有争议性；<br />
<img alt="" src="http://images.cnblogs.com/cnblogs_com/winzheng/UML/Generalization.jpg" height="204" width="293" border="0" /></p>
<h3>实现</h3>
<p>指的是一个class类实现interface接口（可以是多个）的功能；实现是类与接口之间最常见的关系；在Java中此类关系通过关键字implements明确标识，在设计时一般没有争议性；<br />
<img alt="" src="http://images.cnblogs.com/cnblogs_com/winzheng/UML/Realization.jpg" height="203" width="121" border="0" /></p>
<h3>依赖</h3>
<p>可以简单的理解，就是一个类A使用到了另一个类B，而这种使用关系是具有偶然性的、、临时性的、非常弱的，但是B类的变化会影响到A；比如某人要过河，需要借用一条船，此时人与船之间的关系就是依赖；表现在代码层面，为类B作为参数被类A在某个method方法中使用；<br />
<img alt="" src="http://images.cnblogs.com/cnblogs_com/winzheng/UML/Dependence.jpg" height="97" width="430" border="0" /></p>
<h3>关联</h3>
<p>他体现的是两个类、或者类与接口之间语义级别的一种强依赖关系，比如我和我的朋友；这种关系比依赖更强、不存在依赖关系的偶然性、关系也不是临时性
的，一般是长期性的，而且双方的关系一般是平等的、关联可以是单向、双向的；表现在代码层面，为被关联类B以类属性的形式出现在关联类A中，也可能是关联
类A引用了一个类型为被关联类B的全局变量；<br />
<img alt="" src="http://images.cnblogs.com/cnblogs_com/winzheng/UML/Association.jpg" height="105" width="430" border="0" /></p>
<h3>聚合</h3>
<p>聚合是关联关系的一种特例，他体现的是整体与部分、拥有的关系，即has-a的关系，此时整体与部分之间是可分离的，他们可以具有各自的生命周期，
部分可以属于多个整体对象，也可以为多个整体对象共享；比如计算机与CPU、公司与员工的关系等；表现在代码层面，和关联关系是一致的，只能从语义级别来
区分；<br />
<img alt="" src="http://images.cnblogs.com/cnblogs_com/winzheng/UML/Aggregation.jpg" height="108" width="430" border="0" /></p>
<h3>组合</h3>
<p>组合也是关联关系的一种特例，他体现的是一种contains-a的关系，这种关系比聚合更强，也称为强聚合；他同样体现整体与部分间的关系，但此
时整体与部分是不可分的，整体的生命周期结束也就意味着部分的生命周期结束；比如你和你的大脑；表现在代码层面，和关联关系是一致的，只能从语义级别来区
分；<br />
<img alt="" src="http://images.cnblogs.com/cnblogs_com/winzheng/UML/Composition.jpg" height="106" width="430" border="0" /></p>
<p>对于继承、实现这两种关系没多少疑问，他们体现的是一种类与类、或者类与接口间的纵向关系；其他的四者关系则体现的是类与类、或者类与接口间的引
用、横向关系，是比较难区分的，有很多事物间的关系要想准备定位是很难的，前面也提到，这几种关系都是语义级别的，所以从代码层面并不能完全区分各种关
系；但总的来说，后几种关系所表现的强弱程度依次为：组合&gt;聚合&gt;关联&gt;依赖；</p>
<img src ="http://www.blogjava.net/sealyu/aggbug/305420.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2009-12-10 14:47 <a href="http://www.blogjava.net/sealyu/archive/2009/12/10/305420.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>迪米特法则（Law of Demeter）</title><link>http://www.blogjava.net/sealyu/archive/2009/12/10/305415.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Thu, 10 Dec 2009 06:16:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2009/12/10/305415.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/305415.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2009/12/10/305415.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/305415.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/305415.html</trackback:ping><description><![CDATA[<p><font color="#ff0000">迪米特法则（LoD）：</font>又称<font color="#ff0000">最少知识原则（LKP），</font>就是说一个对象应当对其他对象尽可能少的了解。<br />
</p>
<p><br />
<font color="#ff0000">狭义的迪米特法则:</font></p>
<p>如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用.如果其中一个类需要调用另一个类的方法的话,可以通过第三者转发这个调用.</p>
<p><font color="#ff0000">缺点:</font></p>
<p>会在系统内造出大量的小方法,散落在系统的各个角落.这些方法仅仅是传递间接的调用,因此系统与系统中的商业逻辑无关.当设计师试图从一张类图看出总体的构架时,这些小方法会造成迷惑和困扰.</p>
<p>为了克服狭义迪米特法则的缺点,可以使用<font color="#ff0000">依赖倒转原则</font>,引入一个抽象的类型引用"抽象陌生人"对象,使"某人"依赖于"抽象陌生人",换言之,就是将"抽象陌生人"变成朋友.<br />
</p>
<p><br />
<font color="#ff0000">广义的迪米特法则:</font></p>
<p>一个模块设计得好坏的一个重要的标志就是该模块在多大的程度上将自己的内部数据与实现有关的细节隐藏起来.</p>
<p>信息的隐藏非常重要的原因在于,它可以使各个子系统之间脱耦,从而允许它们独立地被开发,优化,使用阅读以及修改.</p>
<p>迪米特法则的主要用意是控制信息的过载.在运用迪米特法则到系统的设计中时,要注意以下几点:</p>
<p>* 在类的划分上,应当创建有弱耦合的类.类之间的耦合越弱,就越有利于复用.</p>
<p>* 在类的结构设计上,每一个类都应当尽量降低成员的访问权限. </p>
<p>* 在类的设计上,只要可能,一个类应当设计成不变类.</p>
<p>* 在对其他类的引用上,一个对象对其他对象的引用应降到最低.</p>
<p>* 尽量限制局部变量的有效范围.</p>
<img src ="http://www.blogjava.net/sealyu/aggbug/305415.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2009-12-10 14:16 <a href="http://www.blogjava.net/sealyu/archive/2009/12/10/305415.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>DCL（双检测锁定-Double Checked Lock）安全性分析(转)</title><link>http://www.blogjava.net/sealyu/archive/2009/12/09/305301.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Wed, 09 Dec 2009 09:01:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2009/12/09/305301.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/305301.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2009/12/09/305301.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/305301.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/305301.html</trackback:ping><description><![CDATA[<div>
<script type="text/javascript">
document.body.oncopy = function() {
if (window.clipboardData) {
setTimeout(function() {
var text = clipboardData.getData("text");
if (text && text.length > 300) {
text = text + ""r"n"n本文来自CSDN博客，转载请标明出处：" + location.href;
clipboardData.setData("text", text);
}
}, 100);
}
}
</script>
<script type="text/javascript">                        function StorePage() { d = document; t = d.selection ? (d.selection.type != 'None' ? d.selection.createRange().text : '') : (d.getSelection ? d.getSelection() : ''); void (keyit = window.open('http://www.365key.com/storeit.aspx?t=' + escape(d.title) + '&u=' + escape(d.location.href) + '&c=' + escape(t), 'keyit', 'scrollbars=no,width=475,height=575,left=75,top=20,status=no,resizable=yes')); keyit.focus(); }</script>
<p>在
水木上看到一篇分析DCL（双检测锁定-Double Checked
Lock）安全性的文章。记得以前也讨论过这个问题，但是为什么DCL也存在隐患，至今没弄明白，这篇分析做了比较详尽的解释，概括起来原因有两点：一是
因为编译器或者处理器并不是严格按照程序顺序进行指令调度。二是java中同步机制的存在。</p>
<p>原文来自：</p>
<p>发信人: wyxzellux (I still believe...), 信区: Java<br />
标 &nbsp;题: Singleton模式与双检测锁定(DCL)<br />
发信站: 水木社区 (Mon Apr &nbsp;7 23:42:14 2008), 站内</p>
<p>看OOP教材时，提到了一个双检测锁定(Double-Checked Lock, DCL)的问题，但是书上没有多介绍，只是说这是一个和底层内存机制有关的漏洞。查阅了下相关资料，对这个问题大致有了点了解。<br />
从头开始说吧。<br />
在多线程的情况下Singleton模式会遇到不少问题，一个简单的例子<br />
<br />
&nbsp;&nbsp; 1: &nbsp;class Singleton { &nbsp; &nbsp; &nbsp;<br />
&nbsp;&nbsp; 2: &nbsp; &nbsp; &nbsp;private static Singleton instance = null; &nbsp; &nbsp; &nbsp;<br />
&nbsp;&nbsp; 3: &nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp;&nbsp; 4: &nbsp; &nbsp; &nbsp;public static Singleton instance() { &nbsp; &nbsp; &nbsp;<br />
&nbsp;&nbsp; 5: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if (instance == null) { &nbsp; &nbsp; &nbsp;<br />
&nbsp;&nbsp; 6: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;instance = new Singleton(); &nbsp; &nbsp; &nbsp;<br />
&nbsp;&nbsp; 7: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;} &nbsp; &nbsp; &nbsp;<br />
&nbsp;&nbsp; 8: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;return instance; &nbsp; &nbsp; <br />
&nbsp;&nbsp; 9: &nbsp; &nbsp; &nbsp;} &nbsp; &nbsp; <br />
&nbsp;&nbsp; 10: &nbsp;} <br />
&nbsp;&nbsp; <br />
假
设这样一个场景，有两个线程调用Singleton.instance()，首先线程一判断instance是否等于null，判断完后一瞬间虚拟机把线
程二调度为运行线程，线程二再次判断instance是否为null，然后创建一个Singleton实例，线程二的时间片用完后，线程一被唤醒，接下来
它执行的代码依然是instance = new Singleton(); <br />
两次调用返回了不同的对象，出现问题了。<br />
<br />
最简单的方法自然是在类被载入时就初始化这个对象：private static Singleton instance = new Singleton();<br />
<br />
JLS(Java Language Specification)中规定了一个类只会被初始化一次，所以这样做肯定是没问题的。<br />
<br />
但是如果要实现延迟初始化(Lazy initialization)，比如这个实例初始化时的参数要在运行期才能确定，应该怎么做呢？<br />
<br />
依然有最简单的方法：使用synchronized关键字修饰初始化方法：<br />
<br />
&nbsp;&nbsp; &nbsp;public synchronized static Singleton instance() { &nbsp; &nbsp; &nbsp; &nbsp;<br />
&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;if (instance == null) {<br />
&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;instance = new Singleton();<br />
&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;return instance;<br />
&nbsp;&nbsp; &nbsp;} <br />
&nbsp;&nbsp; &nbsp;<br />
这里有一个性能问题：多个线程同时访问这个方法时，会因为同步而导致每次只有一个线程运行，影响程序性能。而事实上初始化完毕后只需要简单的返回instance的引用就行了。<br />
<br />
DCL是一个&#8220;看似&#8221;有效的解决方法，先把对应代码放上来吧：<br />
<br />
&nbsp;&nbsp; &nbsp;1 : &nbsp; class Singleton { &nbsp; <br />
&nbsp;&nbsp; &nbsp;2 : &nbsp; &nbsp; &nbsp; private static Singleton instance = null ; &nbsp; <br />
&nbsp;&nbsp; &nbsp;3 : &nbsp; &nbsp; &nbsp;<br />
&nbsp;&nbsp; &nbsp;4 : &nbsp; &nbsp; &nbsp; public static Singleton instance() { &nbsp; <br />
&nbsp;&nbsp; &nbsp;5 : &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (instance == null ) {<br />
&nbsp;&nbsp; &nbsp;6 : &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; synchronized (this) { &nbsp; <br />
&nbsp;&nbsp; &nbsp;7 : &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (instance == null)<br />
&nbsp;&nbsp; &nbsp;8 : &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;instance = new Singleton();<br />
&nbsp;&nbsp; &nbsp;9 : &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;10 : &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;11 : &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;return instance;<br />
&nbsp;&nbsp; &nbsp;12 : &nbsp; &nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;13 : &nbsp;} <br />
<br />
用JavaWorld上对应文章的标题来评论这种做法就是smart, but broken。来看原因：<br />
<br />
Java
编译器为了提高程序性能会进行指令调度，CPU在执行指令时同样出于性能会乱序执行（至少现在用的大多数通用处理器都是out-of-order的），另
外cache的存在也会改变数据回写内存时的顺序[2]。JMM(Java Memory Model,
见[1])指出所有的这些优化都是允许的，只要运行结果和严格按顺序执行所得的结果一样即可。<br />
<br />
Java假设每个线程都跑在自己的处理器
上，享有自己的内存，和共享的主存交互。注意即使在单核上这种模型也是有意义的，考虑到cache和寄存器会保存部分临时变量。理论上每个线程修改自己的
内存后，必须立即更新对应的主存内容。但是Java设计师们认为这种约束会影响程序性能，他们试着创造了一套让程序跑得更快、但又保证线程之间的交互与预
期一致的内存模型。<br />
<br />
synchronized关键字便是其中一把利器。事实上，synchronized块的实现和Linux中的信号量
(semaphore)还是有区别的，前者过程中锁的获得和释放都会都会引发一次Memory
Barrier来强制线程本地内存和主存之间的同步。通过这个机制，Java中的同步机制保证了synchronized块中指令的原子性
(atomic)。<br />
<br />
好了，回过头来看DCL问题。看起来访问一个未同步的instance字段不会产生什么问题，我们再次来假设一个场景：<br />
<br />
线程一进入同步块，执行instance = new Singleton(); 线程二刚开始执行getResource();<br />
<br />
按照顺序的话，接下来应该执行的步骤是 1) 分配新的Singleton对象的内存 2) 调用Singleton的构造器，初始化成员字段 3) instance被赋为指向新的对象的引用。<br />
<br />
前
面说过，编译器或处理器都为了提高性能都有可能进行指令的乱序执行，线程一的真正执行步骤可能是1) 分配内存 2) instance指向新对象
3)
初始化新实例。如果线程二在2完成后3执行前被唤醒，它看到了一个不为null的instance，跳出方法体走了，带着一个还没初始化的
Singleton对象。<br />
<br />
错误发生的一种情形就是这样，关于更详细的编译器指令调度导致的问题，可以参看这个网页 [4]。<br />
<br />
[3] 中提供了一个编译器指令调度的证据<br />
<br />
instance = new Singleton(); 这条命令在Symantec JIT中被编译成<br />
<br />
0206106A &nbsp; mov &nbsp; &nbsp; &nbsp; &nbsp; eax,0F97E78h<br />
0206106F &nbsp; call &nbsp; &nbsp; &nbsp; &nbsp;01F6B210 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;; 分配空间<br />
02061074 &nbsp; mov &nbsp; &nbsp; &nbsp; &nbsp; dword ptr [ebp],eax &nbsp; &nbsp; &nbsp; ; EBP中保存了instance的地址 <br />
<br />
02061077 &nbsp; mov &nbsp; &nbsp; &nbsp; &nbsp; ecx,dword ptr [eax] &nbsp; &nbsp; &nbsp; ; 解引用，获得新的指针地址 <br />
<br />
02061079 &nbsp; mov &nbsp; &nbsp; &nbsp; &nbsp; dword ptr [ecx],100h &nbsp; &nbsp; &nbsp;; 接下来四行是inline后的构造器<br />
0206107F &nbsp; mov &nbsp; &nbsp; &nbsp; &nbsp; dword ptr [ecx+4],200h &nbsp; &nbsp;<br />
02061086 &nbsp; mov &nbsp; &nbsp; &nbsp; &nbsp; dword ptr [ecx+8],400h<br />
0206108D &nbsp; mov &nbsp; &nbsp; &nbsp; &nbsp; dword ptr [ecx+0Ch],0F84030h <br />
<br />
可以看到，赋值完成在初始化之前，而这是JLS允许的。<br />
&nbsp;<br />
另
一种情形是，假设线程一安稳地完成Singleton对象的初始化，退出了同步块，并同步了和本地内存和主存。线程二来了，看到一个非空的引用，拿走。注
意线程二没有执行一个Read Barrier，因为它根本就没进后面的同步块。所以很有可能此时它看到的数据是陈旧的。<br />
<br />
还有很多人根据已知的几种提出了一个又一个fix的方法，但最终还是出现了更多的问题。可以参阅[3]中的介绍。<br />
<br />
[5]中还说明了即使把instance字段声明为volatile还是无法避免错误的原因。<br />
<br />
由此可见，安全的Singleton的构造一般只有两种方法，一是在类载入时就创建该实例，二是使用性能较差的synchronized方法。<br />
<br />
(by ZelluX &nbsp;<a href="../../zellux" target="_blank">http://www.blogjava.net/zellux</a> )<br />
<br />
参考资料： <br />
<br />
[1] Java Language Specification, Second Edition, 第17章介绍了Java中线程和内存交互关系的具体细节。<br />
[2] out-of-order与cache的介绍可以参阅Computer System, A Programmer's Perspective的第四、五章。<br />
[3] The "Double-Checked Locking is Broken" Declaration, <a href="http://www.cs.umd.edu/%7Epugh/java/memoryModel/DoubleCheckedLocking.html" target="_blank">http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html</a><br />
[4] Synchronization and the Java Memory Model, <a href="http://gee.cs.oswego.edu/dl/cpj/jmm.html" target="_blank">http://gee.cs.oswego.edu/dl/cpj/jmm.html</a><br />
[5] Double-checked locking: Clever, but broken, <a href="http://www.javaworld.com/javaworld/jw-02-2001/jw-0209-double.html?page=1" target="_blank">http://www.javaworld.com/javaworld/jw-02-2001/jw-0209-double.html?page=1</a><br />
[6] Holub on Patterns, Learning Design Patterns by Looking at Code</p>
</div>
<img src ="http://www.blogjava.net/sealyu/aggbug/305301.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2009-12-09 17:01 <a href="http://www.blogjava.net/sealyu/archive/2009/12/09/305301.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>重构（转载） </title><link>http://www.blogjava.net/sealyu/archive/2008/04/10/192013.html</link><dc:creator>seal</dc:creator><author>seal</author><pubDate>Thu, 10 Apr 2008 15:00:00 GMT</pubDate><guid>http://www.blogjava.net/sealyu/archive/2008/04/10/192013.html</guid><wfw:comment>http://www.blogjava.net/sealyu/comments/192013.html</wfw:comment><comments>http://www.blogjava.net/sealyu/archive/2008/04/10/192013.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sealyu/comments/commentRss/192013.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sealyu/services/trackbacks/192013.html</trackback:ping><description><![CDATA[<font face="Verdana">&nbsp; <a href="http://www.itisedu.com/phrase/200603121222205.html" target="_new">重构</a>（<a href="http://www.itisedu.com/phrase/200604232047545.html" target="_new">Refactoring</a>）就是在不改变<a href="http://www.itisedu.com/phrase/200604232134205.html" target="_new">软件</a>现有功能的基础上，通过调整<a href="http://www.itisedu.com/phrase/200604232224305.html" target="_new">程序</a>代码改善软件的质量、性能，使其程序的<a href="http://www.itisedu.com/phrase/200603061631585.html" target="_new">设计模式</a>和<a href="http://www.itisedu.com/phrase/200604241328115.html" target="_new">架构</a>更趋合理，提高软件的扩展性和维护性。</font>
<p><font face="Verdana">　　也许有人会问，为什么不在项目开始时多花些时间把设计做好，而要以后花时间来重构呢？要知道一个完
美得可以预见未来任何变化的设计，或一个灵活得可以容纳任何扩展的设计是不存在的。系统设计人员对即将着手的项目往往只能从大方向予以把控，而无法知道每
个细枝末节，其次永远不变的就是变化，提出<a href="http://www.itisedu.com/phrase/200603101518295.html" target="_new">需求</a>的用户往往要在软件成型后，始才开始"品头论足"，系统设计人员毕竟不是先知先觉的神仙，功能的变化导致设计的调整再所难免。所以"测试为先，持续重构"作为良好开发习惯被越来越多的人所采纳，测试和重构像黄河的护堤，成为保证软件质量的法宝。</font></p>
<p><font face="Verdana"><strong>一、为什么要重构（Refactoring）</strong></font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在不改变系统功能的情况下，改变系统的实现方式。为什么要这么做？投入精力不用来满足客户关心的需求，而是仅仅改变了软件的实现方式，这是否是在浪费客户的投资呢？</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 重构的重要性要从软件的生命周期说起。软件不同与普通的产品，他是一种智力产品，没有具体的物理形态。一个软件不可能发生物理损耗，界面上的按钮永远不会因为按动次数太多而发生接触不良。那么为什么一个软件制造出来以后，却不能永远使用下去呢？</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
对软件的生命造成威胁的因素只有一个：需求的变更。一个软件总是为解决某种特定的需求而产生，时代在发展，客户的业务也在发生变化。有的需求相对稳定一
些，有的需求变化的比较剧烈，还有的需求已经消失了，或者转化成了别的需求。在这种情况下，软件必须相应的改变。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 考虑到成本和时间等因素，当然不是所有的需求变化都要在<a href="http://www.itisedu.com/phrase/200602281706245.html" target="_new">软件系统</a>中实现。但是总的说来，软件要适应需求的变化，以保持自己的生命力。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
这就产生了一种糟糕的现象：软件产品最初制造出来，是经过精心的设计，具有良好架构的。但是随着时间的发展、需求的变化，必须不断的修改原有的功能、追加
新的功能，还免不了有一些缺陷需要修改。为了实现变更，不可避免的要违反最初的设计构架。经过一段时间以后，软件的架构就千疮百孔了。bug越来越多，越
来越难维护，新的需求越来越难实现，软件的构架对新的需求渐渐的失去支持能力，而是成为一种制约。最后新需求的开发成本会超过开发一个新的软件的成本，这
就是这个软件系统的生命走到尽头的时候。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 重构就能够最大限度的避免这样一种现象。系统发展到一定阶段后，使用重构的方式，不改变系统的外部功能，只对内部的结构进行重新的整理。通过重构，不断的调整系统的结构，使系统对于需求的变更始终具有较强的适应能力。</font></p>
<p><font face="Verdana">　　通过重构可以达到以下的目标：</font></p>
<p><font face="Verdana">　　&#183;持续偏纠和改进软件设计</font></p>
<p><font face="Verdana">　　重构和设计是相辅相成的，它和设计彼此互补。有了重构，你仍然必须做预先的设计，但是不必是最优的设计，只需要一个合理的解决方案就够了，如果没有重构、<a href="http://www.itisedu.com/phrase/200602281641255.html" target="_new">程序设计</a>会逐渐腐败变质，愈来愈像断线的风筝，脱缰的野马无法控制。重构其实就是整理代码，让所有带着发散倾向的代码回归本位。</font></p>
<p><font face="Verdana">　　&#183;使代码更易为人所理解</font></p>
<p><font face="Verdana">　　Martin Flower在《重构》中有一句经典的话："任何一个傻瓜都能写出<a href="http://www.itisedu.com/phrase/200603021438435.html" target="_new">计算机</a>可以理解的程序，只有写出人<a href="http://www.itisedu.com/phrase/200603090857555.html" target="_new">类</a>容易理解的程序才是优秀的程序员。"对此，笔者感触很深，有些程序员总是能够快速编写出可运行的代码，但代码中晦涩的命名使人晕眩得需要紧握坐椅扶手，试想一个新兵到来接手这样的代码他会不会想当逃兵呢？</font></p>
<p><font face="Verdana">　　软件的生命周期往往需要多批程序员来维护，我们往往忽略了这些后来人。为了使代码容易被他人理解，需要在实现软件功能时做许多额外的事件，如清晰的排版布局，简明扼要的注释，其中命名也是一个重要的方面。一个很好的办法就是采用暗喻命名，即以<a href="http://www.itisedu.com/phrase/200603090845215.html" target="_new">对象</a>实现的功能的依据，用形象化或拟人化的手法进行命名，一个很好的态度就是将每个代码元素像新生儿一样命名，也许笔者有点命名偏执狂的倾向，如能荣此雅号，将深以此为幸。</font></p>
<p><font face="Verdana">　　对于那些让人充满迷茫感甚至误导性的命名，需要果决地、大刀阔斧地整容，永远不要手下留情！</font></p>
<p><font face="Verdana">　　&#183;帮助发现隐藏的代码缺陷</font></p>
<p><font face="Verdana">　　孔子说过：温故而知新。重构代码时逼迫你加深理解原先所写的代码。笔者常有写下程序后，却发生对自
己的程序逻辑不甚理解的情景，曾为此惊悚过，后来发现这种症状居然是许多程序员常患的"感冒"。当你也发生这样的情形时，通过重构代码可以加深对原设计的
理解，发现其中的问题和隐患，构建出更好的代码。</font></p>
<p><font face="Verdana">　　&#183;从长远来看，有助于提高编程效率</font></p>
<p><font face="Verdana">　　当你发现解决一个问题变得异常复杂时，往往不是问题本身造成的，而是你用错了方法，拙劣的设计往往导致臃肿的编码。</font></p>
<p><font face="Verdana">　　改善设计、提高可读性、减少缺陷都是为了稳住阵脚。良好的设计是成功的一半，停下来通过重构改进设计，或许会在当前减缓速度，但它带来的后发优势却是不可低估的。</font></p>
<p><font face="Verdana"><strong>二、何时着手重构（Refactoring）</strong></font></p>
<p><font face="Verdana">　　新官上任三把火，开始一个全新的项目时，程序员往往也会燃起三把火：紧锣密鼓、脚不停蹄、加班加点，一支声势浩大的千军万"码"夹裹着程序员激情和扣击键盘的鸣金奋力前行，势如破竹，攻城掠地，直指"黄龙府"。</font></p>
<p><font face="Verdana">　　开发经理是这支浩浩汤汤代码队伍的统帅，他负责这支队伍的命运，当齐恒公站在山顶上看到管仲训练的
队伍整齐划一地前进时，他感叹说"我有这样一支军队哪里还怕没有胜利呢？"。但很遗憾，你手中的这支队伍原本只是散兵游勇，在前进中招兵买马，不断壮大，
所以队伍变形在所难免。当开发经理发觉队伍变形时，也许就是克制住攻克前方山头的诱惑，停下脚步整顿队伍的时候了。 </font></p>
<p><font face="Verdana">　　Kent Beck提出了"代码坏味道"的说法，和我们所提出的"队伍变形"是同样的意思，队伍变形的信号是什么呢？以下列述的代码症状就是"队伍变形"的强烈信号：</font></p>
<p><font face="Verdana">　　&#183;代码中存在重复的代码</font></p>
<p><font face="Verdana">　　中国有118 家整车生产企业，数量几乎等于美、日、欧所有汽车厂家数之和，但是全国的年产量却不及一个外国大汽车公司的产量。重复建设只会导致效率的低效和资源的浪费。</font></p>
<p><font face="Verdana">　　程序代码更是不能搞重复建设，如果同一个类中有相同的代码块，请把它提炼成类的一个独立方法，如果不同类中具有相同的代码，请把它提炼成一个新类，永远不要重复代码。 </font></p>
<p><font face="Verdana"><br />
&#183;过大的类和过长的方法</font></p>
<p><font face="Verdana">　　过大的类往往是类抽象不合理的结果，类抽象不合理将降低了代码的复用率。方法是类王国中的诸侯国，
诸侯国太大势必动摇中央集权。过长的方法由于包含的逻辑过于复杂，错误机率将直线上升，而可读性则直线下降，类的健壮性很容易被打破。当看到一个过长的方
法时，需要想办法将其划分为多个小方法，以便于分而治之。</font></p>
<p><font face="Verdana">　　&#183;牵一毛而需要动全身的修改</font></p>
<p><font face="Verdana">　　当你发现修改一个小功能，或增加一个小功能时，就引发一次代码地震，也许是你的设计抽象度不够理想，功能代码太过分散所引起的。</font></p>
<p><font face="Verdana">　　&#183;类之间需要过多的通讯</font></p>
<p><font face="Verdana">　　A类需要调用B类的过多方法访问B的内部数据，在关系上这两个类显得有点狎昵，可能这两个类本应该在一起，而不应该分家。</font></p>
<p><font face="Verdana">　　&#183;过度耦合的信息链</font></p>
<p><font face="Verdana">　　"计算机是这样一门科学，它相信可以通过添加一个<a href="http://www.itisedu.com/phrase/200603101709095.html" target="_new">中间层</a>解决任何问题"，所以往往中间层会被过多地追加到程序中。如果你在代码中看到需要获取一个信息，需要一个类的方法调用另一个类的方法，层层挂接，就象输油管一样节节相连。这往往是因为衔接层太多造成的，需要查看就否有可移除的中间层，或是否可以提供更直接的调用方法。</font></p>
<p><font face="Verdana">　　&#183;各立山头干革命</font></p>
<p><font face="Verdana">　　如果你发现有两个类或两个方法虽然命名不同但却拥有相似或相同的功能，你会发现往往是因为开发<a href="http://www.itisedu.com/phrase/200603082251135.html" target="_new">团队</a>成员协调不够造成的。笔者曾经写了一个颇好用的字符串处理类，但因为没有及时通告团队其他人员，后来发现项目中居然有三个字符串处理类。革命资源是珍贵的，我们不应各立山头干革命。 </font></p>
<p><font face="Verdana">　　&#183;不完美的设计</font></p>
<p><font face="Verdana">　　在笔者刚完成的一个比对报警项目中，曾安排阿朱开发报警模块，即通过Socket向指定的短信平台、语音平台及<a href="http://www.itisedu.com/phrase/200603082208195.html" target="_new">客户端</a>报
警器插件发送报警报文信息，阿朱出色地完成了这项任务。后来用户又提出了实时比对的需求，即要求第三方系统以报文形式向比对报警系统发送请求，比对报警系
统接收并响应这个请求。这又需要用到Socket报文通讯，由于原来的设计没有将报文通讯模块独立出来，所以无法复用阿朱开发的代码。后来我及时调整了这
个设计，新增了一个报文收发模块，使系统所有的对外通讯都复用这个模块，系统的整体设计也显得更加合理。</font></p>
<p><font face="Verdana">　　每个系统都或多或少存在不完美的设计，刚开始可能注意不到，到后来才会慢慢凸显出来，此时唯有勇于更改才是最好的出路。</font></p>
<p><font face="Verdana">　　&#183;缺少必要的注释</font></p>
<p><font face="Verdana">　　虽然许多<a href="http://www.itisedu.com/phrase/200602281725525.html" target="_new">软件工程</a>的
书籍常提醒程序员需要防止过多注释，但这个担心好象并没有什么必要。往往程序员更感兴趣的是功能实现而非代码注释，因为前者更能带来成就感，所以代码注释
往往不是过多而是过少，过于简单。人的记忆曲线下降的坡度是陡得吓人的，当过了一段时间后再回头补注释时，很容易发生"提笔忘字，愈言且止"的情形。</font></p>
<p><font face="Verdana">　　曾在网上看到过微软的代码注释，其详尽程度让人叹为观止，也从中体悟到了微软成功的一个经验。 </font></p>
<p><font face="Verdana"><strong>三、重构（Refactoring）的难题</strong></font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 学习一种可以大幅提高生产力的新技术时，你总是难以察觉其不适用的场合。通常你在一个特定场景中学习它，这个场景往往是个项目。这种情况下你很难看出什么会造成这种新技术成效不彰或甚至形成危害。十年前，对象技术（<a href="http://www.itisedu.com/phrase/200604231338435.html" target="_new">object</a> tech.）的情况也是如此。那时如果有人问我「何时不要使用对象」，我很难回答。并非我认为对象十全十美、没有局限性 — 我最反对这种盲目态度，而是尽管我知道它的好处，但确实不知道其局限性在哪儿。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 现在，重构的处境也是如此。我们知道重构的好处，我们知道重构可以给我们的工作带来垂手可得的改变。但是我们还没有获得足够的经验，我们还看不到它的局限性。<br />
这
一小节比我希望的要短。暂且如此吧。随着更多人学会重构技巧，我们也将对它有更多了解。对你而言这意味：虽然我坚决认为你应该尝试一下重构，获得它所提供
的利益，但在此同时，你也应该时时监控其过程，注意寻找重构可能引入的问题。请让我们知道你所遭遇的问题。随着对重构的了解日益增多，我们将找出更多解决
办法，并清楚知道哪些问题是真正难以解决的。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a href="http://www.itisedu.com/phrase/200602271218062.html" target="_new">数据库</a>（Databases）</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 「重构」经常出问题的一个领域就是数据库。绝大多数商用程序都与它们背后的<a href="http://www.itisedu.com/phrase/200604231244235.html" target="_new">database</a> schema（数据库表格结构）紧密耦合（coupled）在一起，这也是database schema如此难以修改的原因之一。另一个原因是数据迁移（migration）。就算你非常小心地将系统<a href="http://www.itisedu.com/phrase/200604161254415.html" target="_new">分层</a>（layered），将database schema和对象模型（object model）间的依赖降至最低，但database schema的改变还是让你不得不迁移所有数据，这可能是件漫长而烦琐的工作。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在「非对象数据库」（nonobject
databases）中，解决这个问题的办法之一就是：在对象模型（object model）和数据库模型（database
model）之间插入一个分隔层（separate
layer），这就可以隔离两个模型各自的变化。升级某一模型时无需同时升级另一模型，只需升级上述的分隔层即可。这样的分隔层会增加系统复杂度，但可以
给你很大的灵活度。如果你同时拥有多个数据库，或如果数据库模型较为复杂使你难以控制，那么即使不进行重构，这分隔层也是很重要的。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 你无需一开始就插入分隔层，可以在发现对象模型变得不稳定时再产生它。这样你就可以为你的改变找到最好的杠杆效应。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 对开发者而言，对象数据库既有帮助也有妨碍。某些<a href="http://www.itisedu.com/phrase/200603101726185.html" target="_new">面向对象</a>数
据库提供不同版本的对象之间的自动迁移功能，这减少了数据迁移时的工作量，但还是会损失一定时间。如果各数据库之间的数据迁移并非自动进行，你就必须自行
完成迁移工作，这个工作量可是很大的。这种情况下你必须更加留神classes内的数据结构变化。你仍然可以放心将classes的行为转移过去，但转移
值域（field）时就必须格外小心。数据尚未被转移前你就得先运用访问函数（accessors）造成「数据已经转移」的假象。一旦你确定知道「数据应
该在何处」时，就可以一次性地将数据迁移过去。这时惟一需要修改的只有访问函数（accessors），这也降低了错误风险。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 修改接口（Changing Interfaces）</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 关于对象，另一件重要事情是：它们允许你分开修改软件模块的实现（implementation）和接口（interface）。你可以安全地修改某对象内部而不影响他人，但对于接口要特别谨慎 — 如果接口被修改了，任何事情都有可能发生。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 一直对重构带来困扰的一件事就是：许多重构手法的确会修改接口。像Rename Method（273）这么简单的重构手法所做的一切就是修改接口。这对极为珍贵的封装概念会带来什么影响呢？</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
如果某个函数的所有调用动作都在你的控制之下，那么即使修改函数名称也不会有任何问题。哪怕面对一个public函数，只要能取得并修改其所有调用者，你
也可以安心地将这个函数易名。只有当需要修改的接口系被那些「找不到，即使找到也不能修改」的代码使用时，接口的修改才会成为问题。如果情况真是如此，我
就会说：这个接口是个「已发布接口」（published interface）— 比公开接口（public
interface）更进一步。接口一旦发行，你就再也无法仅仅修改调用者而能够安全地修改接口了。你需要一个略为复杂的程序。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这个想法改变了我们的问题。如今的问题是：该如何面对那些必须修改「已发布接口」的重构手法？</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 简言之，如果重构手法改变了已发布接口（published
interface），你必须同时维护新旧两个接口，直到你的所有用户都有时间对这个变化做出反应。幸运的是这不太困难。你通常都有办法把事情组织好，让
旧接口继续工作。请尽量这么做：让旧接口调用新接口。当你要修改某个函数名称时，请留下旧函数，让它调用新函数。千万不要拷贝函数实现码，那会让你陷入
「重复代码」（duplicated code）的泥淖中难以自拔。你还应该使用Java提供的 deprecation（反对）设施，将旧接口标记为
"deprecated"。这么一来你的调用者就会注意到它了。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这个过程的一个好例子就是Java容器类（collection classes）。Java 2的新容器取代了原先一些容器。当Java 2容器发布时，JavaSoft花了很大力气来为开发者提供一条顺利迁徙之路。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
「保留旧接口」的办法通常可行，但很烦人。起码在一段时间里你必须建造（build）并维护一些额外的函数。它们会使接口变得复杂，使接口难以使用。还好
我们有另一个选择：不要发布（publish）接口。当然我不是说要完全禁止，因为很明显你必得发布一些接口。如果你正在建造供外部使用的APIs，像
Sun所做的那样，肯定你必得发布接口。我之所以说尽量不要发布，是因为我常常看到一些开发团队公开了太多接口。我曾经看到一支三人团队这么工作：每个人
都向另外两人公开发布接口。这使他们不得不经常来回维护接口，而其实他们原本可以直接进入程序库，径行修改自己管理的那一部分，那会轻松许多。过度强调
「代码拥有权」的团队常常会犯这种错误。发布接口很有用，但也有代价。所以除非真有必要，别发布接口。这可能意味需要改变你的代码拥有权观念，让每个人都
可以修改别人的代码，以运应接口的改动。以搭档（成对）编程（Pair <a href="http://www.itisedu.com/phrase/200604232129205.html" target="_new">Programming</a>）完成这一切通常是个好主意。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 不要过早发布（published）接口。请修改你的代码拥有权政策，使重构更顺畅。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
Java之中还有一个特别关于「修改接口」的问题：在throws子句中增加一个异常。这并不是对签名式（signature）的修改，所以你无法以
delegation（委托手法）隐藏它。但如果用户代码不作出相应修改，编译器不会让它通过。这个问题很难解决。你可以为这个函数选择一个新名字，让旧
函数调用它，并将这个新增的checked exception（可控式异常）转换成一个unchecked
exception（不可控异常）。你也可以拋出一个unchecked异常，不过这样你就会失去检验能力。如果你那么做，你可以警告调用者：这个
unchecked异常日后会变成一个checked异常。这样他们就有时间在自己的代码中加上对此异常的处理。出于这个原因，我总是喜欢为整个
package定义一个superclass异常（就像java.<a href="http://www.itisedu.com/phrase/200604022014515.html" target="_new">sql</a>的SQLException），并确保所有public函数只在自己的throws子句中声明这个异常。这样我就可以随心所欲地定义subclass异常，不会影响调用者，因为调用者永远只知道那个更具一般性的superclass异常。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 难以通过重构手法完成的设计改动</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
通过重构，可以排除所有设计错误吗？是否存在某些核心设计决策，无法以重构手法修改？在这个领域里，我们的统计数据尚不完整。当然某些情况下我们可以很有
效地重构，这常常令我们倍感惊讶，但的确也有难以重构的地方。比如说在一个项目中，我们很难（但还是有可能）将「无安全需求（no security
requirements）情况下构造起来的系统」重构为「安全性良好的（good security）系统」。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
这种情况下我的办法就是「先想象重构的情况」。考虑候选设计方案时，我会问自己：将某个设计重构为另一个设计的难度有多大？如果看上去很简单，我就不必太
担心选择是否得当，于是我就会选最简单的设计，哪怕它不能覆盖所有潜在需求也没关系。但如果预先看不到简单的重构办法，我就会在设计上投入更多力气。不过
我发现，这种情况很少出现。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 何时不该重构？</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 有时候你根本不应该重构 — 例如当你应该重新编写所有代码的时候。有时候既有代码实在太混乱，重构它还不如从新写一个来得简单。作出这种决定很困难，我承认我也没有什么好准则可以判断何时应该放弃重构。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 重写（而非重构）的一个清楚讯号就是：现有代码根本不能正常运作。你可能只是试着做点测试，然后就发现代码中满是错误，根本无法稳定运作。记住，重构之前，代码必须起码能够在大部分情况下正常运作。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 一个折衷办法就是：将「大块头软件」重构为「封装良好的小型<a href="http://www.itisedu.com/phrase/200603302222545.html" target="_new">组件</a>」。然后你就可以逐一对组件作出「重构或重建」的决定。这是一个颇具希望的办法，但我还没有足够数据，所以也无法写出优秀的指导原则。对于一个重要的古老系统，这肯定会是一个很好的方向。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
另外，如果项目已近最后期限，你也应该避免重构。在此时机，从重构过程赢得的生产力只有在最后期限过后才能体现出来，而那个时候已经时不我予。Ward
Cunningham对此有一个很好的看法。他把未完成的重构工作形容为「债务」。很多公司都需要借债来使自己更有效地运转。但是借债就得付利息，过于复
杂的代码所造成的「维护和扩展的额外开销」就是利息。你可以承受一定程度的利息，但如果利息太高你就会被压垮。把债务管理好是很重要的，你应该随时通过重
构来偿还一部分债务。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果项目已经非常接近最后期限，你不应该再分心于重构，因为已经没有时间了。不过多个项目经验显示：重构的确能够提高生产力。如果最后你没有足够时间，通常就表示你其实早该进行重构。</font></p>
<p><font face="Verdana"><strong>四、重构（Refactoring）与设计</strong></font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 「重构」肩负一项特别任务：它和设计彼此互补。初学编程的时候，我埋头就写程序，浑浑噩噩地进行开发。然而很快我便发现，「事先设计」（upfront design）可以助我节省回头工的高昂成本。于是我很快加强这种「预先设计」风格。许多人都把设计看作<a href="http://www.itisedu.com/phrase/200603282233345.html" target="_new">软件开发</a>的
关键环节，而把编程（programming）看作只是机械式的低级劳动。他们认为设计就像画工程图而编码就像施工。但是你要知道，软件和真实器械有着很
大的差异。软件的可塑性更强，而且完全是思想产品。正如Alistair Cockburn所说：『有了设计，我可以思考更快，但是其中充满小漏洞。』</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 有一种观点认为：重构可以成为「预先设计」的替代品。这意思是你根本不必做任何设计，只管按照最初想法开始编码，让代码有效运作，然后再将它重构成型。事实上这种办法真的可行。我的确看过有人这么做，最后获得设计良好的软件。<a href="http://www.itisedu.com/phrase/200603071747045.html" target="_new">极限编程</a>（<a href="http://www.itisedu.com/phrase/200604231325295.html" target="_new">Extreme Programming</a>）[Beck, <a href="http://www.itisedu.com/phrase/200604231325145.html" target="_new">XP</a>] 的支持者极力提倡这种办法。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
尽管如上所言，只运用重构也能收到效果，但这并不是最有效的途径。是的，即使极限编程（Extreme
Programming）爱好者也会进行预先设计。他们会使用CRC卡或类似的东西来检验各种不同想法，然后才得到第一个可被接受的解决方案，然后才能开
始编码，然后才能重构。关键在于：重构改变了「预先设计」的角色。如果没有重构，你就必须保证「预先设计」正确无误，这个压力太大了。这意味如果将来需要
对原始设计做任何修改，代价都将非常高昂。因此你需要把更多时间和精力放在预先设计上，以避免日后修改。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
如果你选择重构，问题的重点就转变了。你仍然做预先设计，但是不必一定找出正确的解决方案。此刻的你只需要得到一个足够合理的解决方案就够了。你很肯定地
知道，在实现这个初始解决方案的时候，你对问题的理解也会逐渐加深，你可能会察觉最佳解决方案和你当初设想的有些不同。只要有重构这项武器在手，就不成问
题，因为重构让日后的修改成本不再高昂。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
这种转变导致一个重要结果：软件设计朝向简化前进了一大步。过去未曾运用重构时，我总是力求得到灵活的解决方案。任何一个需求都让我提心吊胆地猜疑：在系
统寿命期间，这个需求会导致怎样的变化？由于变更设计的代价非常高昂，所以我希望建造一个足够灵活、足够强固的解决方案，希望它能承受我所能预见的所有需
求变化。问题在于：要建造一个灵活的解决方案，所需的成本难以估算。灵活的解决方案比简单的解决方案复杂许多，所以最终得到的软件通常也会更难维护 —
虽然它在我预先设想的方向上的确是更加灵活。就算幸运走在预先设想的方向上，你也必须理解如何修改设计。如果变化只出现在一两个地方，那不算大问题。然而
变化其实可能出现在系统各处。如果在所有可能的变化出现地点都建立起灵活性，整个系统的复杂度和维护难度都会大大提高。当然，如果最后发现所有这些灵活性
都毫无必要，这才是最大的失败。你知道，这其中肯定有些灵活性的确派不上用场，但你却无法预测到底是哪些派不上用场。为了获得自己想要的灵活性，你不得不
加入比实际需要更多的灵活性。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
有了重构，你就可以通过一条不同的途径来应付变化带来的风险。你仍旧需要思考潜在的变化，仍旧需要考虑灵活的解决方案。但是你不必再逐一实现这些解决方
案，而是应该问问自己：『把一个简单的解决方案重构成这个灵活的方案有多大难度？』如果答案是「相当容易」（大多数时候都如此），那么你就只需实现目前的
简单方案就行了。<br />
重构可以带来更简单的设计，同时又不损失灵活性，这也降低了设计过程的难度，减轻了设计压力。一旦对重构带来的简单性有更多感
受，你甚至可以不必再预先思考前述所谓的灵活方案 —
一旦需要它，你总有足够的信心去重构。是的，当下只管建造可运行的最简化系统，至于灵活而复杂的设计，唔，多数时候你都不会需要它。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 劳而无获— Ron Jeffries</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Chrysler Comprehensive Compensation（克莱斯勒综合薪资系统）的支付过程太慢了。虽然我们的开发还没结束，这个问题却已经开始困扰我们，因为它已经拖累了测试速度。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Kent Beck、Martin
Fowler和我决定解决这个问题。等待大伙儿会合的时间里，凭着我对这个系统的全盘了解，我开始推测：到底是什么让系统变慢了？我想到数种可能，然后和
伙伴们谈了几种可能的修改方案。最后，关于「如何让这个系统运行更快」，我们提出了一些真正的好点子。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 然后，我们拿Kent的量测工具<a href="http://www.itisedu.com/phrase/200604231331545.html" target="_new">度量</a>了系统性能。我一开始所想的可能性竟然全都不是问题肇因。我们发现：系统把一半时间用来创建「日期」实体（instance）。更有趣的是，所有这些实体都有相同的值。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 于是我们观察日期的创建逻辑，发现有机会将它优化。日期原本是由字符串转换而生，即使无外部输入也是如此。之所以使用字符串转换方式，完全是为了方便键盘输入。好，也许我们可以将它优化。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 于是我们观察日期怎样被这个程序运用。我们发现，很多日期对象都被用来产生「日期区间」实体（instance）。「日期区间」是个对象，由一个起始日期和一个结束日期组成。仔细追踪下去，我们发现绝大多数日期区间是空的！</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 处理日期区间时我们遵循这样一个规则：如果结束日期在起始日期之前，这个日期区间就该是空的。这是一条很好的规则，完全符合这个<a href="http://www.itisedu.com/phrase/200604231359565.html" target="_new">class</a>的需要。采用此一规则后不久，我们意识到，创建一个「起始日期在结束日期之后」的日期区间，仍然不算是清晰的代码，于是我们把这个行为提炼到一个factory method（译注：一个著名的设计<a href="http://www.itisedu.com/phrase/200603061709535.html" target="_new">模式</a>，见《<a href="http://www.itisedu.com/phrase/200604241216065.html" target="_new">Design Pattern</a>s》），由它专门创建「空的日期区间」。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
我们做了上述修改，使代码更加清晰，却意外得到了一个惊喜。我们创建一个固定不变的「空日期区间」对象，并让上述调整后的factory
method每次都返回该对象，而不再每次都创建新对象。这一修改把系统速度提升了几乎一倍，足以让测试速度达到可接受程度。这只花了我们大约五分钟。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我和团队成员（Kent和Martin谢绝参加）认真推测过：我们了若指掌的这个程序中可能有什么错误？我们甚至凭空做了些改进设计，却没有先对系统的真实情况进行量测。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我们完全错了。除了一场很有趣的交谈，我们什么好事都没做。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 教训：哪怕你完全了解系统，也请实际量测它的性能，不要臆测。臆测会让你学到一些东西，但十有八九你是错的。</font></p>
<p><font face="Verdana"><strong>五、重构与性能（Performance）</strong></font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
译注：在我的接触经验中，performance一词被不同的人予以不同的解释和认知：效率、性能、效能。不同地区（例如台湾和大陆）的习惯用法亦不相
同。本书一遇performance我便译为性能。efficient译为高效，effective译为有效。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
关于重构，有一个常被提出的问题：它对程序的性能将造成怎样的影响？为了让软件易于理解，你常会作出一些使程序运行变慢的修改。这是个重要的问题。我并不
赞成为了提高设计的纯洁性或把希望寄托于更快的硬件身上，而忽略了程序性能。已经有很多软件因为速度太慢而被用户拒绝，日益提高的机器速度亦只不过略微放
宽了速度方面的限制而已。但是，换个角度说，虽然重构必然会使软件运行更慢，但它也使软件的性能优化更易进行。除了对性能有严格要求的实时（real
time）系统，其它任何情况下「编写快速软件」的秘密就是：首先写出可调（tunable）软件，然后调整它以求获得足够速度。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我看过三种「编写快速软件」的方法。其中最严格的是「时间预算法」（time
budgeting），这通常只用于性能要求极高的实时系统。如果使用这种方法，分解你的设计时就要做好预算，给每个组件预先分配一定资源 —
包括时间和执行轨迹（footprint）。每个组件绝对不能超出自己的预算，就算拥有「可在不同组件之间调度预配时间」的机制也不行。这种方法高度重视
性能，对于心律调节器一类的系统是必须的，因为在这样的系统中迟来的数据就是错误的数据。但对其他类系统（例如我经常开发的企业<a href="http://www.itisedu.com/phrase/200603011147495.html" target="_new">信息系统</a>）而言，如此追求高性能就有点过份了。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 第二种方法是「持续关切法」（constant
attention）。这种方法要求任何程序员在任何时间做任何事时，都要设法保持系统的高性能。这种方式很常见，感觉上很有吸引力，但通常不会起太大作
用。任何修改如果是为了提高性能，通常会使程序难以维护，因而减缓开发速度。如果最终得到的软件的确更快了，那么这点损失尚有所值，可惜通常事与愿违，因
为性能改善一旦被分散到程序各角落，每次改善都只不过是从「对程序行为的一个狭隘视角」出发而已。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
关于性能，一件很有趣的事情是：如果你对大多数程序进行分析，你会发现它把大半时间都耗费在一小半代码身上。如果你一视同仁地优化所有代码，90%
的优化工作都是白费劲儿，因为被你优化的代码有许多难得被执行起来。你花时间做优化是为了让程序运行更快，但如果因为缺乏对程序的清楚认识而花费时间，那
些时间都是被浪费掉了。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 第三种性能提升法系利用上述的 "90%"
统计数据。采用这种方法时，你以一种「良好的分解方式」（well-factored
manner）来建造自己的程序，不对性能投以任何关切，直至进入性能优化阶段 —
那通常是在开发后期。一旦进入该阶段，你再按照某个特定程序来调整程序性能。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;
在性能优化阶段中，你首先应该以一个量测工具监控程序的运行，让它告诉你程序中哪些地方大量消耗时间和空间。这样你就可以找出性能热点（hot
spot）所在的一小段代码。然后你应该集中关切这些性能热点，并使用前述「持续关切法」中的优化手段来优化它们。由于你把注意力都集中在热点上，较少的
工作量便可显现较好的成果。即便如此你还是必须保持谨慎。和重构一样，你应该小幅度进行修改。每走一步都需要编译、测试、再次量测。如果没能提高性能，就
应该撤销此次修改。你应该继续这个「发现热点、去除热点」的过程，直到获得客户满意的性能为止。关于这项技术，McConnell
[McConnell] 为我们提供了更多信息。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
一个被良好分解（well-factored）的程序可从两方面帮助此种优化形式。首先，它让你有比较充裕的时间进行性能调整（performance
tuning），因为有分解良好的代码在手，你就能够更快速地添加功能，也就有更多时间用在性能问题上（准确的量测则保证你把这些时间投资在恰当地点）。
其次，面对分解良好的程序，你在进行性能分析时便有较细的粒度（granularity），于是量测工具把你带入范围较小的程序段落中，而性能的调整也比
较容易些。由于代码更加清晰，因此你能够更好地理解自己的选择，更清楚哪种调整起关键作用。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我发现重构可以帮助我写出更快的软件。短程看来，重构的确会使软件变慢，但它使优化阶段中的软件性能调整更容易。最终我还是有赚头。</font></p>
<p><font face="Verdana"><strong>六、重构起源何处？</strong></font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我曾经努力想找出重构（refactoring）一词的真正起源，但最终失败了。优秀程序员肯定至少会花一些时间来清理自己的代码。这么做是因为，他们知道简洁的代码比杂乱无章的代码更容易修改，而且他们知道自己几乎无法一开始就写出简洁的代码。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
重构不止如此。本书中我把重构看作整个软件开发过程的一个关键环节。最早认识重构重要性的两个人是Ward Cunningham和Kent
Beck，他们早在1980s之前就开始使用Smalltalk，那是个特别适合重构的环境。Smalltalk是一个十分动态的环境，你可以很快写出极
具功能的软件。Smalltalk的「编译/连结/执行」周期非常短，因此很容易快速修改代码。它是面向对象，所以也能够提供强大工具，最大限度地将修改
的影响隐藏于定义良好的接口背后。Ward和Kent努力发展出一套适合这类环境的软件开发过程（如今Kent把这种风格叫作极限编程 [Beck,
XP]）。他们意识到：重构对于提高他们的生产力非常重要。从那时起他们就一直在工作中运用重构技术，在严肃而认真的软件项目中使用它，并不断精炼这个程
序。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
Ward和Kent的思想对Smalltalk社群产生了极大影响，重构概念也成为Smalltalk文化中的一个重要元素。Smalltalk社群的另
一位领袖是Ralph Johnson，伊利诺斯大学乌尔班纳分校教授，著名的「四巨头」 [Gang of Four]
之一。Ralph最大的兴趣之一就是开发软件<a href="http://www.itisedu.com/phrase/200603061723295.html" target="_new">框架</a>（<a href="http://www.itisedu.com/phrase/200604241001145.html" target="_new">framework</a>）。他揭示了重构对于灵活高效框架的开发帮助。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp; Bill
Opdyke是Ralph的博士研究生，对框架也很感兴趣。他看到重构的潜在价值，并看到重构应用于Smalltalk之外的其它语言的可能性。他的技术
背景是电话交换系统的开发。在这种系统中，大量的复杂情况与时俱增，而且非常难以修改。Bill的博士研究就是从工具构筑者的角度来看待重构。通过研究，
Bill发现：在C++
framework开发项目中，重构很有用。他也研究了极有必要的「语义保持性（semantics-preserving）重构」及其证明方式，以及如
何以工具实现重构。时至今日，Bill的博士论文 [Opdyke] 仍然是重构领域中最有价值、最丰硕的研究成果。此外他为本书撰写了第13章。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
我还记得1992年OOPSLA大会上见到Bill的情景。我们坐在一间咖啡厅里，讨论当时我正为保健业务构筑的一个概念框架（conceptual
framework）中的某些工作。Bill跟我谈起他的研究成果，我还记得自己当时的想法：『有趣，但并非真的那么重要』。唉，我完全错了。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; John Brant和Don Roberts将重构中的「工具」构想发扬光大，开发了一个名为「重构浏览器」（Refactoring Browser）的Smalltalk重构工具。他们撰写了本书第14章，其中对重构工具做了更多介绍。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
那么，我呢？我一直有清理代码的倾向，但从来没有想到这会有那么重要。后来我和Kent一起做了个项目，看到他使用重构手法，也看到重构对生产性能和产品
质量带来的影响。这份体验让我相信：重构是一门非常重要的技术。但是，在重构的学习和推广过程中我遇到了挫折，因为我拿不出任何一本书给程序员看，也没有
任何一位专家打算写出这样一本书。所以，在这些专家的帮助下，我写下了这本书。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 优化一个薪资系统— Rich Garzaniti<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
将Chrysler Comprehensive
Compensation（克莱斯勒综合薪资系统）交给GemStone公司之前，我们用了相当长的时间开发它。开发过程中我们无可避免地发现程序不够
快，于是找了Jim Haungs — GemSmith中的一位好手 — 请他帮我们优化这个系统。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Jim先用一点时间让他的团队了解系统运作方式，然后以GemStone的ProfMonitor特性编写出一个性能量测工具，将它插入我们的<a href="http://www.itisedu.com/phrase/200604241204115.html" target="_new">功能测试</a>中。这个工具可以显示系统产生的对象数量，以及这些对象的诞生点。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
令我们吃惊的是：创建量最大的对象竟是字符串。其中最大的工作量则是反复产生12,000-bytes的字符串。这很特别，因为这字符串实在太大了，连
GemStone惯用的垃圾回收设施都无法处理它。由于它是如此巨大，每当被创建出来，GemStone都会将它分页（paging）至磁盘上。也就是说
字符串的创建竟然用上了I/O<a href="http://www.itisedu.com/phrase/200604161433025.html" target="_new">子系统</a>（译注：分页机制会动用I/O），而每次输出记录时都要产生这样的字符串三次﹗</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我们的第一个解决办法是把一个12,000-bytes字符串缓存（cached）起来，这可解决一大半问题。后来我们又加以修改，将它直接写入一个file stream，从而避免产生字符串。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 解决了「巨大字符串」问题后，Jim的量测工具又发现了一些类似问题，只不过字符串稍微小一些：800-bytes、500-bytes&#8230;&#8230;等等，我们也都对它们改用file stream，于是问题都解决了。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 使用这些技术，我们稳步提高了系统性能。开发过程中原本似乎需要1,000小时以上才能完成的薪资计算，实际运作时只花40小时。一个月后我们把时间缩短到18小时。正式投入运转时只花12小时。经过一年的运行和改善后，全部计算只需9小时。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我们的最大改进就是：将程序放在多处理器（multi-processor）计算器上，以多<a href="http://www.itisedu.com/phrase/200603091754305.html" target="_new">线程</a>（multiple threads）方式运行。最初这个系统并非按照多线程思维来设计，但由于代码有良好分解（well factored），所以我们只花三天时间就让它得以同时运行多个线程了。现在，薪资的计算只需2小时。</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在Jim提供工具使我们得以在实际操作中量度系统性能之前，我们也猜测过问题所在。但如果只靠猜测，我们需要很长的时间才能试出真正的解法。真实的量测指出了一个完全不同的方向，并大大加快了我们的进度。<br />
</font></p>
<table>
    <tbody>
        <tr>
            <td><br />
            </td>
        </tr>
    </tbody>
</table>
<img src ="http://www.blogjava.net/sealyu/aggbug/192013.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sealyu/" target="_blank">seal</a> 2008-04-10 23:00 <a href="http://www.blogjava.net/sealyu/archive/2008/04/10/192013.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>