<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>我想网 &#187; Patten</title>
	<atom:link href="http://www.iwanna.cn/topics/develope/patten/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.iwanna.cn</link>
	<description></description>
	<lastBuildDate>Sat, 31 Jul 2010 15:12:14 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>类的设计原则</title>
		<link>http://www.iwanna.cn/archives/2010/07/05/4365/</link>
		<comments>http://www.iwanna.cn/archives/2010/07/05/4365/#comments</comments>
		<pubDate>Mon, 05 Jul 2010 14:06:17 +0000</pubDate>
		<dc:creator>seasun</dc:creator>
				<category><![CDATA[Patten]]></category>
		<category><![CDATA[程序开发]]></category>
		<category><![CDATA[Programmer]]></category>

		<guid isPermaLink="false">http://www.iwanna.cn/?p=4365</guid>
		<description><![CDATA[开闭原则Software entities (classes, modules, function, etc.) should be open for  extension, but closed for modification.
软件实体（模块，类，方法等）应该对扩展开放，对修改关闭。
开闭原则（OCP：Open-Closed Principle）是指在进行面向对象设计（OOD：Object Oriented  Design）中，设计类或其他程序单位时，应该遵循：
- 对扩展开放（open）
- 对修改关闭（closed）
的设计原则。
开闭原则是判断面向对象设计是否正确的最基本的原理之一。

根据开闭原则，在设计一个软件系统模块（类，方法）的时候，应该可以在不修改原有的模块（修改关闭）的基础上，能扩展其功能（扩展开放）。
-  扩展开放：某模块的功能是可扩展的，则该模块是扩展开放的。软件系统的功能上的可扩展性要求模块是扩展开放的。
-  修改关闭：某模块被其他模块调用，如果该模块的源代码不允许修改，则该模块修改关闭的。软件系统的功能上的稳定性，持续性要求是修改关闭的。
这也是系统设计需要遵循开闭原则的原因：
1）稳定性。开闭原则要求扩展功能不修改原来的代码，这可以让软件系统在变化中保持稳定。
2）扩展性。开闭原则要求对扩展开放，通过扩展提供新的或改变原有的功能，让软件系统具有灵活的可扩展性。
遵循开闭原则的系统设计，可以让软件系统可复用，并且易于维护。
开闭原则的实现方法
为了满足开闭原则的  对修改关闭（closed for modification） 原则以及扩展开放（open for extension）  原则，应该对软件系统中的不变的部分加以抽象，在面向对象的设计中，
-  可以把这些不变的部分加以抽象成不变的接口，这些不变的接口可以应对未来的扩展；
-  接口的最小功能设计原则。根据这个原则，原有的接口要么可以应对未来的扩展；不足的部分可以通过定义新的接口来实现；
-  模块之间的调用通过抽象接口进行，这样即使实现层发生变化，也无需修改调用方的代码。
接口可以被复用，但接口的实现却不一定能被复用。接口是稳定的，关闭的，但接口的实现是可变的，开放的。可以通过对接口的不同实现以及类的继承行为等为系统增加新的或改变系统原来的功能，实现软件系统的柔软扩展。
简单地说，软件系统是否有良好的接口（抽象）设计是判断软件系统是否满足开闭原则的一种重要的判断基准。现在多把开闭原则等同于面向接口的软件设计。
开闭原则的相对性
软件系统的构建是一个需要不断重构的过程，在这个过程中，模块的功能抽象，模块与模块间的关系，都不会从一开始就非常清晰明了，所以构建100%满足开闭原则的软件系统是相当困难的，这就是开闭原则的相对性。但在设计过程中，通过对模块功能的抽象（接口定义），模块之间的关系的抽象（通过接口调用），抽象与实现的分离（面向接口的程序设计）等，可以尽量接近满足开闭原则。
单一职责原则前言
Robert  C. Martin氏为我们总结了在面向对象的设计（OOD）中应该遵循的原则，这些原则被称为“Principles of OOD”，关于“Principles  of OOD”的相关文章可以从Object Menter得到。
本文介绍“Principles of OOD”中的单一职责原则：Single  Responsibility Principle [...]]]></description>
			<content:encoded><![CDATA[<p>开闭原则Software entities (classes, modules, function, etc.) should be open for  extension, but closed for modification.<br />
软件实体（模块，类，方法等）应该对扩展开放，对修改关闭。<br />
开闭原则（OCP：Open-Closed Principle）是指在进行面向对象设计（OOD：Object Oriented  Design）中，设计类或其他程序单位时，应该遵循：<br />
- 对扩展开放（open）<br />
- 对修改关闭（closed）<br />
的设计原则。<br />
开闭原则是判断面向对象设计是否正确的最基本的原理之一。<br />
<span id="more-4365"></span><br />
根据开闭原则，在设计一个软件系统模块（类，方法）的时候，应该可以在不修改原有的模块（修改关闭）的基础上，能扩展其功能（扩展开放）。<br />
-  扩展开放：某模块的功能是可扩展的，则该模块是扩展开放的。软件系统的功能上的可扩展性要求模块是扩展开放的。<br />
-  修改关闭：某模块被其他模块调用，如果该模块的源代码不允许修改，则该模块修改关闭的。软件系统的功能上的稳定性，持续性要求是修改关闭的。<br />
这也是系统设计需要遵循开闭原则的原因：<br />
1）稳定性。开闭原则要求扩展功能不修改原来的代码，这可以让软件系统在变化中保持稳定。<br />
2）扩展性。开闭原则要求对扩展开放，通过扩展提供新的或改变原有的功能，让软件系统具有灵活的可扩展性。<br />
遵循开闭原则的系统设计，可以让软件系统可复用，并且易于维护。<br />
<strong>开闭原则的实现方法</strong><br />
为了满足开闭原则的  对修改关闭（closed for modification） 原则以及扩展开放（open for extension）  原则，应该对软件系统中的不变的部分加以抽象，在面向对象的设计中，<br />
-  可以把这些不变的部分加以抽象成不变的接口，这些不变的接口可以应对未来的扩展；<br />
-  接口的最小功能设计原则。根据这个原则，原有的接口要么可以应对未来的扩展；不足的部分可以通过定义新的接口来实现；<br />
-  模块之间的调用通过抽象接口进行，这样即使实现层发生变化，也无需修改调用方的代码。<br />
接口可以被复用，但接口的实现却不一定能被复用。接口是稳定的，关闭的，但接口的实现是可变的，开放的。可以通过对接口的不同实现以及类的继承行为等为系统增加新的或改变系统原来的功能，实现软件系统的柔软扩展。<br />
简单地说，软件系统是否有良好的接口（抽象）设计是判断软件系统是否满足开闭原则的一种重要的判断基准。现在多把开闭原则等同于面向接口的软件设计。<br />
<strong>开闭原则的相对性</strong><br />
软件系统的构建是一个需要不断重构的过程，在这个过程中，模块的功能抽象，模块与模块间的关系，都不会从一开始就非常清晰明了，所以构建100%满足开闭原则的软件系统是相当困难的，这就是开闭原则的相对性。但在设计过程中，通过对模块功能的抽象（接口定义），模块之间的关系的抽象（通过接口调用），抽象与实现的分离（面向接口的程序设计）等，可以尽量接近满足开闭原则。<br />
<strong>单一职责原则</strong><strong>前言</strong><br />
Robert  C. Martin氏为我们总结了在面向对象的设计（OOD）中应该遵循的原则，这些原则被称为“Principles of OOD”，关于“Principles  of OOD”的相关文章可以从Object Menter得到。<br />
本文介绍“Principles of OOD”中的单一职责原则：Single  Responsibility Principle (SRP)。<br />
可以从这里查看<a href="http://www.objectmentor.com/resources/articles/srp.pdf" target="_blank">Single Responsibility Principle (SRP)的原文</a><img src="http://images.uheed.com/iwanna/2010/07/05/edit_arrow6.gif" border="0" alt="" />。<br />
<strong>概要</strong><br />
There should never be more than one reason for a  class to  change.<br />
永远不要让一个类存在多个改变的理由。<br />
换句话说，如果一个类需要改变，改变它的理由永远只有一个。如果存在多个改变它的理由，就需要重新设计该类。<br />
SRP（Single  Responsibility  Principle）原则的核心含意是：只能让一个类有且仅有一个职责。这也是单一职责原则的命名含义。<br />
<strong>为什么一个类不能有多于一个以上的职责呢？</strong><br />
如果一个类具有一个以上的职责，那么就会有多个不同的原因引起该类变化，而这种变化将影响到该类不同职责的使用者（不同用户）：<br />
1，一方面，如果一个职责使用了外部类库，则使用另外一个职责的用户却也不得不包含这个未被使用的外部类库。<br />
2，另一方面，某个用户由于某个原因需要修改其中一个职责，另外一个职责的用户也将受到影响，他将不得不重新编译和配置。<br />
这违反了设计的开闭原则，也不是我们所期望的。<br />
<strong>职责的划分</strong><br />
既然一个类不能有多个职责，那么怎么划分职责呢？<br />
Robert.C  Martin给出了一个著名的定义：所谓一个类的一个职责是指引起该类变化的一个原因。<br />
If you can think of more than one  motive for changing a class, then that class has more than one  responsibility.<br />
如果你能想到一个类存在多个使其改变的原因，那么这个类就存在多个职责。<br />
<a href="http://www.objectmentor.com/resources/articles/srp.pdf" target="_blank">Single Responsibility Principle (SRP)的原文</a><img src="http://images.uheed.com/iwanna/2010/07/05/edit_arrow6.gif" border="0" alt="" />里举了一个Modem的例子来说明怎么样进行职责的划分，这里我们也沿用这个例子来说明一下：<br />
SRP违反例：<br />
Modem.<a href="http://www.iwanna.cn/tags/java/" class="st_tag internal_tag" rel="tag nofollow" title="Posts tagged with Java">java</a><br />
interface  Modem {<br />
public void dial(String pno);    //拨号<br />
public void  hangup();        //挂断<br />
public void send(char c);    //发送数据<br />
public  char  recv();        //接收数据<br />
}<br />
咋一看，这是一个没有任何问题的接口设计。但事实上，这个接口包含了2个职责：第一个是连接管理（dial,  hangup）；另一个是数据通信（send,  recv）。很多情况下，这2个职责没有任何共通的部分，它们因为不同的理由而改变，被不同部分的程序调用。<br />
所以它违反了SRP原则。<br />
下面的类图将它的2个不同职责分成2个不同的接口，这样至少可以让客户端应用程序使用具有单一职责的接口：<br />
<img src="http://images.uheed.com/iwanna/2010/07/05/ShowImagez.jpg" border="0" alt="" /><br />
让ModemImplementation实现这两个接口。我们注意到，ModemImplementation又组合了2个职责，这不是我们希望的，但有时这又是必须的。通常由于某些原因，迫使我们不得不绑定多个职责到一个类中，但我们至少可以通过接口的分割来分离应用程序关心的概念。<br />
事实上，这个例子一个更好的设计应该是这样的，如图：<br />
<img src="http://images.uheed.com/iwanna/2010/07/05/ShowImagex.jpg" border="0" alt="" /><br />
<strong>小结</strong><br />
Single Responsibility Principle  (SRP)从职责（改变理由）的侧面上为我们对类（接口）的抽象的颗粒度建立了判断基准：在为系统设计类（接口）的时候应该保证它们的单一职责性。<br />
接口分隔原则<strong>前言</strong><br />
Robert  C. Martin氏为我们总结了在面向对象的设计（OOD）中应该遵循的原则，这些原则被称为“Principles of OOD”，关于““Principles  of OOD”的相关文章可以从<a href="http://www.objectmentor.com/" target="_blank">Object  Menter</a><img src="http://images.uheed.com/iwanna/2010/07/05/edit_arrow6.gif" border="0" alt="" />得到。</p>
<p>本文介绍“Principles of OOD”中的接口分隔原则：Interface Segregation  Principle (ISP)。</p>
<p>可以从这里查看<a href="http://www.objectmentor.com/resources/articles/isp.pdf" target="_blank">Interface Segregation Principle (ISP)的原文</a><img src="http://images.uheed.com/iwanna/2010/07/05/edit_arrow6.gif" border="0" alt="" />。<br />
<strong>概要</strong><br />
Clients should not be forced to depend upon  interfaces that they do not  use.<br />
不能强迫用户去依赖那些他们不使用的接口。换句话说，使用多个专门的接口比使用单一的总接口总要好。<br />
它包含了2层意思：<br />
-  接口的设计原则：接口的设计应该遵循最小接口原则，不要把用户不使用的方法塞进同一个接口里。<br />
如果一个接口的方法没有被使用到，则说明该接口过胖，应该将其分割成几个功能专一的接口。<br />
-  接口的依赖（继承）原则：如果一个接口a依赖（继承）另一个接口b，则接口a相当于继承了接口b的方法，那么继承了接口b后的接口a也应该遵循上述原则：不应该包含用户不使用的方法。<br />
反之，则说明接口a被b给污染了，应该重新设计它们的关系。<br />
如果用户被迫依赖他们不使用的接口，当接口发生改变时，他们也不得不跟着改变。换而言之，一个用户依赖了未使用但被其他用户使用的接口，当其他用户修改该接口时，依赖该接口的所有用户都将受到影响。这显然违反了开闭原则，也不是我们所期望的。<br />
下面我们举例说明怎么设计接口或类之间的关系，使其不违反ISP原则。<br />
假如有一个Door，有lock，unlock功能，另外，可以在Door上安装一个Alarm而使其具有报警功能。用户可以选择一般的Door，也可以选择具有报警功能的Door。<br />
有以下几种设计方法：<br />
ISP原则的违反例：<br />
方法一：<br />
在Door接口里定义所有的方法。图：<br />
<img src="http://images.uheed.com/iwanna/2010/07/05/ShowImagec.jpg" border="0" alt="" /><br />
但这样一来，依赖Door接口的CommonDoor却不得不实现未使用的alarm()方法。违反了ISP原则。<br />
方法二：<br />
在Alarm接口定义alarm方法，在Door接口定义lock，unlock方法，Door接口继承Alarm接口。<br />
<img src="http://images.uheed.com/iwanna/2010/07/05/ShowImagev.jpg" border="0" alt="" /><br />
跟方法一一样，依赖Door接口的CommonDoor却不得不实现未使用的alarm()方法。违反了ISP原则。<br />
遵循ISP原则的例：<br />
方法三：通过多重继承实现<br />
<img src="http://images.uheed.com/iwanna/2010/07/05/ShowImageb.jpg" border="0" alt="" /><br />
在Alarm接口定义alarm方法，在Door接口定义lock，unlock方法。接口之间无继承关系。CommonDoor实现Door接口，<br />
AlarmDoor有2种实现方案：<br />
1），同时实现Door和Alarm接口。<br />
2），继承CommonDoor，并实现Alarm接口。该方案是继承方式的Adapter设计模式的实现。<br />
第2）种方案更具有实用性。<br />
这种设计遵循了ISP设计原则。<br />
方法四：通过委让实现<br />
<img src="http://images.uheed.com/iwanna/2010/07/05/ShowImagen.jpg" border="0" alt="" /><br />
这种方法其实是委让方式的Adapter设计模式的实现。<br />
在这种方法里，AlarmDoor实现了Alarm接口，同时把功能lock和unlock委让给CommonDoor对象完成。<br />
这种设计遵循了ISP设计原则。<br />
<strong>小结</strong><br />
Interface  Segregation Principle  (ISP)从对接口的使用上为我们对接口抽象的颗粒度建立了判断基准：在为系统设计接口的时候，使用多个专门的接口代替单一的胖接口。<br />
依赖倒置原则Robert  C. Martin氏为我们总结了在面向对象的设计（OOD）中应该遵循的原则，这些原则被称为“Principles of OOD”，关于“Principles  of OOD”的相关文章可以从<a href="http://www.objectmentor.com/" target="_blank">Object  Menter</a><img src="http://images.uheed.com/iwanna/2010/07/05/edit_arrow6.gif" border="0" alt="" />得到。</p>
<p>本文介绍DIP：Dependency Inversion Principle &#8211; 依赖倒置原则。<br />
有关<a href="http://www.objectmentor.com/resources/articles/dip.pdf" target="_blank">Dependency Inversion Principle (DIP) 原文</a><img src="http://images.uheed.com/iwanna/2010/07/05/edit_arrow6.gif" border="0" alt="" />可以从<a href="http://www.objectmentor.com/resources/articles/dip.pdf" target="_blank">这里</a><img src="http://images.uheed.com/iwanna/2010/07/05/edit_arrow6.gif" border="0" alt="" /> 得到。<br />
该文提出了<strong>依赖倒置原则的2个重要方针</strong>：<br />
A. High level modules should not  depend upon low level modules. Both should depend upon abstractions.<br />
B.  Abstractions should not depend upon details. Details should depend upon  abstractions.<br />
中文意思为：<br />
A. 高层模块不应该依赖于低层模块，二者都应该依赖于抽象<br />
B.  抽象不应该依赖于细节，细节应该依赖于抽象<br />
<strong>概念解说：</strong><br />
依赖：在程序设计中，如果一个模块a使用/调用了另一个模块b，我们称模块a依赖模块b。<br />
高层模块与低层模块：往往在一个应用程序中，我们有一些低层次的类，这些类实现了一些基本的或初级的操作，我们称之为低层模块；另外有一些高层次的类，这些类封装了某些复杂的逻辑，并且依赖于低层次的类，这些类我们称之为高层模块。<br />
为什么叫做依赖倒置（Dependency  Inversion）呢？<br />
面向对象程序设计相对于面向过程（结构化）程序设计而言，依赖关系被倒置了。因为传统的结构化程序设计中，高层模块总是依赖于低层模块。<br />
<strong>问题的提出：</strong><br />
Robert  C. Martin氏在原文中给出了“Bad Design”的定义：<br />
1. It is hard to change because every  change affects too many other parts of the  system.<br />
(Rigidity)<br />
系统很难改变，因为每个改变都会影响其他很多部分。<br />
2. When you make a change,  unexpected parts of the system break.  (Fragility)<br />
当你对某地方做一修改，系统的看似无关的其他部分都不工作了。<br />
3. It is hard to reuse in  another application because it cannot be disentangled from<br />
the current  application. (Immobility)<br />
系统很难被另外一个应用重用，因为你很难将要重用的部分从系统中分离开来。<br />
导致“Bad  Design”的很大原因是“高层模块”过分依赖“低层模块”。<br />
一个良好的设计应该是系统的每一部分都是可替换的。<br />
如果“高层模块”过分依赖“低层模块”，一方面一旦“低层模块”需要替换或者修改，“高层模块”将受到影响；另一方面，高层模块很难可以重用。<br />
比如，一个Copy模块，需要把来自Keyboard的输入复制到Print，即使对Keyboard和Print的封装已经做得非常好，但如果Copy模块里直接使用Keyboard与Print，Copy任很难被其他应用环境（比如需要输出到磁盘时）重用。<br />
<strong>问题的解决：</strong><br />
为了解决上述问题，Robert  C. Martin氏提出了OO设计的Dependency Inversion Principle (DIP)  原则。<br />
DIP给出了一个解决方案：在高层模块与低层模块之间，引入一个抽象接口层。<br />
High Level Classes（高层模块） &#8211;&gt;  Abstraction Layer（抽象接口层） &#8211;&gt; Low Level  Classes（低层模块）<br />
抽象接口是对低层模块的抽象，低层模块继承或实现该抽象接口。<br />
这样，高层模块不直接依赖低层模块，高层模块与低层模块都依赖抽象接口层。<br />
当然，抽象也不依赖低层模块的实现细节，低层模块依赖（继承或实现）抽象定义。<br />
Robert  C. Martin氏给出的DIP方案的类的结构图：<br />
<img src="http://images.uheed.com/iwanna/2010/07/05/ShowImagem.jpg" border="0" alt="" /><br />
PolicyLayer&#8211;&gt;MechanismInterface(abstract)&#8211;MechanismLayer&#8211;&gt;UtilityInterface(abstract)&#8211;UtilityLayer<br />
类与类之间都通过Abstract  Layer来组合关系。<br />
里氏替换原则Robert C.  Martin氏为我们总结了在面向对象的设计（OOD）中应该遵循的原则，这些原则被称为“Principles of OOD”，关于“Principles of  OOD”的相关文章可以从<a href="http://www.objectmentor.com/" target="_blank">Object  Menter</a><img src="http://images.uheed.com/iwanna/2010/07/05/edit_arrow6.gif" border="0" alt="" />得到。</p>
<p>本文介绍“Principles of OOD”中的里氏替换原则：Liskov Substitution  Principle (LSP)。</p>
<p>可以从这里<a href="http://www.objectmentor.com/resources/articles/lsp.pdf" target="_blank">查看Liskov Substitution Principle (LSP)的原文</a> <img src="http://images.uheed.com/iwanna/2010/07/05/edit_arrow6.gif" border="0" alt="" />。<br />
<strong>里氏替换原则LSP的概念解说</strong><br />
Functions that use pointers or references  to base classes must be able to use objects of derived classes without knowing  it.<br />
所有引用基类的地方必须能透明地使用其子类的对象。也就是说，只有满足以下2个条件的OO设计才可被认为是满足了LSP原则：<br />
-  不应该在代码中出现if/else之类对子类类型进行判断的条件。以下代码就违反了LSP定义。<br />
if (obj typeof Class1) {<br />
do  something<br />
} else if (obj typeof Class2) {<br />
do something else<br />
}<br />
-  子类应当可以替换父类并出现在父类能够出现的任何地方，或者说如果我们把代码中使用基类的地方用它的子类所代替，代码还能正常工作。<br />
里氏替换原则LSP是使代码符合开闭原则的一个重要保证。同时LSP体现了：<br />
-  类的继承原则：如果一个继承类的对象可能会在基类出现的地方出现运行错误，则该子类不应该从该基类继承，或者说，应该重新设计它们之间的关系。<br />
-  动作正确性保证：从另一个侧面上保证了符合LSP设计原则的类的扩展不会给已有的系统引入新的错误。<br />
类的继承原则：<br />
Robert C.  Martin氏在介绍Liskov Substitution Principle  (LSP)的原文里，举了Rectangle和Square的例子。这里沿用这个例子，但用<a href="http://www.iwanna.cn/tags/java/" class="st_tag internal_tag" rel="tag nofollow" title="Posts tagged with Java">Java</a>语言对其加以重写，并忽略了某些细节只列出下面的精要部分来说明  里氏替换原则 对类的继承上的约束。<br />
代码：<br />
<img src="http://images.uheed.com/iwanna/2010/07/05/dfgwwww.jpg" border="0" alt="" /><br />
这里Rectangle是基类，Square从Rectangle继承。<br />
这种继承关系有什么问题吗？<br />
假如已有的系统中存在以下既有的业务逻辑代码：</p>
<p>void  g(Rectangle r) {<br />
r.setWidth(5);<br />
r.setHeight(4);<br />
if (r.getWidth() *  r.getHeight() != 20) {<br />
throw new  RuntimeException();<br />
}<br />
}<br />
则对应于扩展类Square，在调用既有业务逻辑时：</p>
<p>Rectangle  square = new  Square();<br />
g(square);<br />
时会抛出一个RuntimeException异常。这显然违反了LSP原则。<br />
动作正确性保证：<br />
因为LSP对子类的约束，所以为已存在的类做扩展构造一个新的子类时，根据LSP的定义，不会给已有的系统引入新的错误。<br />
<strong>Design  by Contract</strong><br />
根据Bertrand Meyer氏提出的Design by  Contract（DBC：基于合同的设计）概念的描述，对于类的一个方法，都有一个前提条件以及一个后续条件，前提条件说明方法接受什么样的参数数据等，只有前提条件得到满足时，这个方法才能被调用；同时后续条件用来说明这个方法完成时的状态，如果一个方法的执行会导致这个方法的后续条件不成立，那么这个方法也不应该正常返回。<br />
现在把前提条件以及后续条件应用到继承子类中，子类方法应该满足：<br />
1）前提条件不强于基类．<br />
2）后续条件不弱于基类．<br />
换句话说，通过基类的接口调用一个对象时，用户只知道基类前提条件以及后续条件。因此继承类不得要求用户提供比基类方法要求的更强的前提条件，亦即，继承类方法必须接受任何基类方法能接受的任何条件（参数）。同样，继承类必须顺从基类的所有后续条件，亦即，继承类方法的行为和输出不得违反由基类建立起来的任何约束，不能让用户对继承类方法的输出感到困惑。<br />
这样，我们就有了基于合同的LSP，基于合同的LSP是LSP的一种强化。<br />
在很多情况下，在设计初期我们类之间的关系不是很明确，LSP则给了我们一个判断和设计类之间关系的基准：需不需要继承，以及怎样设计继承关系。<br />
<strong>参考资料：</strong><br />
<a href="http://www.objectmentor.com/resources/articles/lsp.pdf" target="_blank">Liskov Substitution Principle (LSP)的原文 </a></p>
<hr />
<p>© <a href="http://www.iwanna.cn">我想网</a> Akon 所有 , 2010. |
<a href="http://www.iwanna.cn/archives/2010/07/05/4365/">永久链接</a> |
<a href="http://www.iwanna.cn/archives/2010/07/05/4365/#comments">没有评论</a> |
提交到
<a rel="nofollow" target="_blank" href="http://www.google.com/reader/view/feed/http://www.iwanna.cn/archives/2010/07/05/4365/">Google Reader</a>
<a rel="nofollow" target="_blank" href="http://www.xianguo.com/subscribe.php?url=http://www.iwanna.cn/archives/2010/07/05/4365/">鲜果</a>
<a rel="nofollow" target="_blank" href="http://www.zhuaxia.com/add_channel.php?url=http://www.iwanna.cn/archives/2010/07/05/4365/">抓虾</a>
<hr />
</p>
	标签：<a href="http://www.iwanna.cn/topics/develope/patten/" title="Patten" rel="tag nofollow">Patten</a>, <a href="http://www.iwanna.cn/tags/patten/" title="Patten" rel="tag nofollow">Patten</a>, <a href="http://www.iwanna.cn/tags/programmer/" title="Programmer" rel="tag nofollow">Programmer</a>, <a href="http://www.iwanna.cn/topics/develope/" title="程序开发" rel="tag nofollow">程序开发</a><br />

	<h2 class="related_post">您可能会感兴趣的其他文章</h2>
	<ul class="st-related-posts">
	<li><a href="http://www.iwanna.cn/archives/2010/06/12/3907/" title="适配器模式 (2010年06月12日)">适配器模式</a> </li>
	<li><a href="http://www.iwanna.cn/archives/2010/05/28/3527/" title="软件开发者的软实力：沟通与协作－《程序员》官网 (2010年05月28日)">软件开发者的软实力：沟通与协作－《程序员》官网</a> </li>
	<li><a href="http://www.iwanna.cn/archives/2009/04/02/149/" title="设计模式之单例模式 (2009年04月2日)">设计模式之单例模式</a> </li>
	<li><a href="http://www.iwanna.cn/archives/2010/06/23/4157/" title="解码 Web 开发语言 (2010年06月23日)">解码 Web 开发语言</a> </li>
	<li><a href="http://www.iwanna.cn/archives/2009/11/11/2381/" title="给敏捷软件开发的26条建议 (2009年11月11日)">给敏捷软件开发的26条建议</a> </li>
	<li><a href="http://www.iwanna.cn/archives/2010/06/20/4100/" title="程序在内存中运行的奥秘 (2010年06月20日)">程序在内存中运行的奥秘</a> </li>
	<li><a href="http://www.iwanna.cn/archives/2009/04/16/411/" title="程序员的八种境界 (2009年04月16日)">程序员的八种境界</a> </li>
	<li><a href="http://www.iwanna.cn/archives/2010/06/24/4174/" title="程序员应该为这样的代码感到惭愧 (2010年06月24日)">程序员应该为这样的代码感到惭愧</a> </li>
	<li><a href="http://www.iwanna.cn/archives/2009/04/07/216/" title="程序员失业后应做的五件事 (2009年04月7日)">程序员失业后应做的五件事</a> </li>
	<li><a href="http://www.iwanna.cn/archives/2010/07/04/4357/" title="献给计算机专业的同学 (2010年07月4日)">献给计算机专业的同学</a> </li>
</ul>


<p><small>Feed enhanced by <a href='http://planetozh.com/blog/my-projects/wordpress-plugin-better-feed-rss/'>Better Feed</a> from  <a href='http://planetozh.com/blog/'>Ozh</a></small></p>
]]></content:encoded>
			<wfw:commentRss>http://www.iwanna.cn/archives/2010/07/05/4365/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>适配器模式</title>
		<link>http://www.iwanna.cn/archives/2010/06/12/3907/</link>
		<comments>http://www.iwanna.cn/archives/2010/06/12/3907/#comments</comments>
		<pubDate>Fri, 11 Jun 2010 16:13:17 +0000</pubDate>
		<dc:creator>seasun</dc:creator>
				<category><![CDATA[Patten]]></category>

		<guid isPermaLink="false">http://www.iwanna.cn/?p=3907</guid>
		<description><![CDATA[Adapter（别名Wrapper）模式：将一个类的接口，转换成客户期望的另一个类的接口。适配器让原本接口不兼容的类可以合作无间。
要点：
1． 适配器模式主要应用于“希望复用一些现存的类，但是接口又与复用环境要求不一致的情况”，在遗留代码复用、类库迁移等方面非常有用。
2．  适配器模式有对象适配器和类适配器两种形式的实现结构，但是类适配器采用“多继承”的实现方式，带来了不良的高耦合，所以一般不推荐使用。对象适配器采用 “对象组合”的方式，更符合松耦合精神。
实现：


类的适配器模式结构图（继承）

对象的适配器模式结构图（组合）
（对象适配器的代码实现）
Target：定义Client使用的与特定领域相关的接口

public interface Target
{
    void request();
}

Adaptee：现在需要适配的已经存在的接口

public class Adaptee
{
    public void specificRequest(){}
}

Adapter：对Adaptee 的接口与Target接口进行适配

public class Adapter implements Target
{
    public Adapter(Adaptee adaptee)
    {
        super();
        this.adaptee = adaptee;
   [...]]]></description>
			<content:encoded><![CDATA[<p>Adapter（别名Wrapper）模式：将一个类的接口，转换成客户期望的另一个类的接口。适配器让原本接口不兼容的类可以合作无间。</p>
<p>要点：</p>
<p>1． 适配器模式主要应用于“希望复用一些现存的类，但是接口又与复用环境要求不一致的情况”，在遗留代码复用、类库迁移等方面非常有用。</p>
<p>2．  适配器模式有对象适配器和类适配器两种形式的实现结构，但是类适配器采用“多继承”的实现方式，带来了不良的高耦合，所以一般不推荐使用。对象适配器采用 “对象组合”的方式，更符合松耦合精神。</p>
<p>实现：</p>
<p><a href="http://images.uheed.com/iwanna/2010/06/12/clip_image002_thumb.jpg" target="_blank"><img title="clip_image002" src="http://images.uheed.com/iwanna/2010/06/12/clip_image002_thumb.jpg" border="0" alt="clip_image002" width="428" height="217" /></a><br />
<span id="more-3907"></span><br />
类的适配器模式结构图（继承）</p>
<p><a href="http://images.cnblogs.com/cnblogs_com/god_bless_you/WindowsLiveWriter/b65763e0c958_6D1A/clip_image004_2.jpg" target="_blank"><img title="clip_image004" src="http://images.cnblogs.com/cnblogs_com/god_bless_you/WindowsLiveWriter/b65763e0c958_6D1A/clip_image004_thumb.jpg" border="0" alt="clip_image004" width="425" height="220" /></a></p>
<p>对象的适配器模式结构图（组合）</p>
<p>（对象适配器的代码实现）</p>
<p>Target：定义Client使用的与特定领域相关的接口</p>
<div>
<pre>public interface Target
{
    void request();
}</pre>
</div>
<p>Adaptee：现在需要适配的已经存在的接口</p>
<div>
<pre>public class Adaptee
{
    public void specificRequest(){}
}</pre>
</div>
<p>Adapter：对Adaptee 的接口与Target接口进行适配</p>
<div>
<pre>public class Adapter implements Target
{
    public Adapter(Adaptee adaptee)
    {
        super();
        this.adaptee = adaptee;
    }

    public void request()
    {
        adaptee.specificRequest();
    }

    private Adaptee adaptee;
}</pre>
</div>
<p>适用性：</p>
<p>1. 系统需要使用现有的类，而此类的接口不符合系统的需要。</p>
<p>2.  想要建立一个可以重复使用的类，用于与一些彼此之间没有太大关联的一些类，包括一些可能在将来引进的类一起工作。这些源类不一定有很复杂的接口。</p>
<p>3. （对对象适配器而言）在设计里，需要改变多个已有子类的接口，如果使用类的适配器模式，就要针对每一个子类做一个适配器，而这不太实际。</p>
<p>效果及优缺点：</p>
<p>对于类适配器：</p>
<p>1.  用一个具体的Adapter类对Adaptee和Taget进行匹配。结果是当我们想要匹配一个类以及所有它的子类时，类Adapter将不能胜任工作。</p>
<p>2. 使得Adapter可以override（重定义） Adaptee的部分行为，因为Adapter是Adaptee的一个子类。</p>
<p>对于对象适配器：</p>
<p>1.  允许一个Adapter与多个Adaptee，即Adaptee本身以及它的所有子类（如果有子类的话）同时工作。Adapter也可以一次给所有的 Adaptee添加功能。</p>
<p>2. 使得override（重定义）Adaptee的行为比较困难。如果一定要override  Adaptee的方法，就只好先做一个Adaptee的子类以override  Adaptee的方法，然后再把这个子类当作真正的Adaptee源进行适配。</p>
<hr />
<p>© <a href="http://www.iwanna.cn">我想网</a> Akon 所有 , 2010. |
<a href="http://www.iwanna.cn/archives/2010/06/12/3907/">永久链接</a> |
<a href="http://www.iwanna.cn/archives/2010/06/12/3907/#comments">没有评论</a> |
提交到
<a rel="nofollow" target="_blank" href="http://www.google.com/reader/view/feed/http://www.iwanna.cn/archives/2010/06/12/3907/">Google Reader</a>
<a rel="nofollow" target="_blank" href="http://www.xianguo.com/subscribe.php?url=http://www.iwanna.cn/archives/2010/06/12/3907/">鲜果</a>
<a rel="nofollow" target="_blank" href="http://www.zhuaxia.com/add_channel.php?url=http://www.iwanna.cn/archives/2010/06/12/3907/">抓虾</a>
<hr />
</p>
	标签：<a href="http://www.iwanna.cn/topics/develope/patten/" title="Patten" rel="tag nofollow">Patten</a>, <a href="http://www.iwanna.cn/tags/patten/" title="Patten" rel="tag nofollow">Patten</a><br />

	<h2 class="related_post">您可能会感兴趣的其他文章</h2>
	<ul class="st-related-posts">
	<li><a href="http://www.iwanna.cn/archives/2009/04/02/149/" title="设计模式之单例模式 (2009年04月2日)">设计模式之单例模式</a> </li>
	<li><a href="http://www.iwanna.cn/archives/2010/07/05/4365/" title="类的设计原则 (2010年07月5日)">类的设计原则</a> </li>
	<li><a href="http://www.iwanna.cn/archives/2009/04/01/134/" title="JavaScript事件设计模式 (2009年04月1日)">JavaScript事件设计模式</a> </li>
</ul>


<p><small>Feed enhanced by <a href='http://planetozh.com/blog/my-projects/wordpress-plugin-better-feed-rss/'>Better Feed</a> from  <a href='http://planetozh.com/blog/'>Ozh</a></small></p>
]]></content:encoded>
			<wfw:commentRss>http://www.iwanna.cn/archives/2010/06/12/3907/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>设计模式之单例模式</title>
		<link>http://www.iwanna.cn/archives/2009/04/02/149/</link>
		<comments>http://www.iwanna.cn/archives/2009/04/02/149/#comments</comments>
		<pubDate>Thu, 02 Apr 2009 05:00:34 +0000</pubDate>
		<dc:creator>seasun</dc:creator>
				<category><![CDATA[.Net]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Patten]]></category>
		<category><![CDATA[我想想想]]></category>

		<guid isPermaLink="false">http://www.iwanna.cn/?p=149</guid>
		<description><![CDATA[1         单例模式的日常应用
我们在浏览BBS、SNS网站的时候，常常会看到“当前在线人数”这样的一项内容。对于
这样的一项功能，我们通常的做法是把当前的在线人数存放到一个内存、文件或者数据库中，每次用户登录的时候，就会马上从内存、文件或者数据库中取出，在其基础上加1后，作为当前的在线人数进行显示，然后再把它保存回内存、文件或者数据库里，这样后续登录的用户看到的就是更新后的当前在线人数；同样的道理，当用户退出后，当前在线人数进行减1的工作。所以，对于这样的一个需求，我们按照面向对象的设计思想，可以把它抽象为“在线计数器”这样一个对象，具体实现如下：

Java代码：
//在线人数计数器
class OnlineCounter {
//在线人数
private int onlineCount = 0;
//构造函数
public OnlineCounter(){
//从文件或者数据库读取数据,假如读出来的数据是100
this.onlineCount = 100;
}
//在用户登录后，在线人数加1
public void incCount(){
this.onlineCount++;
}
//在用户退出后，在线人数减1
public void decCount(){
this.onlineCount&#8211;;
}
//保存在线人数
public void saveCount(){
}
//获取在线人数
public int getCount(){
return onlineCount;
}
//测试函数
public static void main(String[] args) {
try{
OnlineCounter onlineCounter = new OnlineCounter();
System.out.println(&#8220;在线人数：&#8221; +onlineCounter.getCount());
onlineCounter.incCount();
System.out.println(&#8220;在线人数：&#8221; + onlineCounter.getCount());
onlineCounter.decCount();
System.out.println(&#8220;在线人数：&#8221; + onlineCounter.getCount());
}catch(Exception err){
}
}
}
.Net代码：
//在线人数计数器
class OnlineCounter{
//在线人数
private int onlineCount = 0;
//构造函数
public OnlineCounter(){
//从文件或者数据库读取数据,假如读出来的数据是100
this.onlineCount = 100;
}
//在用户登录后，在线人数加1
public void incCount(){
this.onlineCount++;
}
//在用户退出后，在线人数减1
public void decCount(){
this.onlineCount&#8211;;
}
//保存在线人数
public [...]]]></description>
			<content:encoded><![CDATA[<p>1         单例模式的日常应用</p>
<p>我们在浏览BBS、SNS网站的时候，常常会看到“当前在线人数”这样的一项内容。对于</p>
<p>这样的一项功能，我们通常的做法是把当前的在线人数存放到一个内存、文件或者数据库中，每次用户登录的时候，就会马上从内存、文件或者数据库中取出，在其基础上加1后，作为当前的在线人数进行显示，然后再把它保存回内存、文件或者数据库里，这样后续登录的用户看到的就是更新后的当前在线人数；同样的道理，当用户退出后，当前在线人数进行减1的工作。所以，对于这样的一个需求，我们按照面向对象的设计思想，可以把它抽象为“在线计数器”这样一个对象，具体实现如下：</p>
<p><span id="more-149"></span></p>
<p><a href="http://www.iwanna.cn/tags/java/" class="st_tag internal_tag" rel="tag nofollow" title="Posts tagged with Java">Java</a>代码：</p>
<p>//在线人数计数器</p>
<p>class OnlineCounter {</p>
<p>//在线人数</p>
<p>private int onlineCount = 0;</p>
<p>//构造函数</p>
<p>public OnlineCounter(){</p>
<p>//从文件或者数据库读取数据,假如读出来的数据是100</p>
<p>this.onlineCount = 100;</p>
<p>}</p>
<p>//在用户登录后，在线人数加1</p>
<p>public void incCount(){</p>
<p>this.onlineCount++;</p>
<p>}</p>
<p>//在用户退出后，在线人数减1</p>
<p>public void decCount(){</p>
<p>this.onlineCount&#8211;;</p>
<p>}</p>
<p>//保存在线人数</p>
<p>public void saveCount(){</p>
<p>}</p>
<p>//获取在线人数</p>
<p>public int getCount(){</p>
<p>return onlineCount;</p>
<p>}</p>
<p>//测试函数</p>
<p>public static void main(String[] args) {</p>
<p>try{</p>
<p>OnlineCounter onlineCounter = new OnlineCounter();</p>
<p>System.out.println(&#8220;在线人数：&#8221; +onlineCounter.getCount());</p>
<p>onlineCounter.incCount();</p>
<p>System.out.println(&#8220;在线人数：&#8221; + onlineCounter.getCount());</p>
<p>onlineCounter.decCount();</p>
<p>System.out.println(&#8220;在线人数：&#8221; + onlineCounter.getCount());</p>
<p>}catch(Exception err){</p>
<p>}</p>
<p>}</p>
<p>}</p>
<p>.Net代码：</p>
<p>//在线人数计数器</p>
<p>class OnlineCounter{</p>
<p>//在线人数</p>
<p>private int onlineCount = 0;</p>
<p>//构造函数</p>
<p>public OnlineCounter(){</p>
<p>//从文件或者数据库读取数据,假如读出来的数据是100</p>
<p>this.onlineCount = 100;</p>
<p>}</p>
<p>//在用户登录后，在线人数加1</p>
<p>public void incCount(){</p>
<p>this.onlineCount++;</p>
<p>}</p>
<p>//在用户退出后，在线人数减1</p>
<p>public void decCount(){</p>
<p>this.onlineCount&#8211;;</p>
<p>}</p>
<p>//保存在线人数</p>
<p>public void saveCount(){</p>
<p>return onlineCount;</p>
<p>}</p>
<p>//获取在线人数</p>
<p>public int getCount(){</p>
<p>return onlineCount;</p>
<p>}</p>
<p>//测试函数</p>
<p>public static void Main(string[] args) {</p>
<p>OnlineCounter onlineCounter = new OnlineCounter();</p>
<p>Console.WriteLine(&#8220;在线人数：&#8221; + onlineCounter.getCount());</p>
<p>onlineCounter.incCount();</p>
<p>Console.WriteLine(&#8220;在线人数：&#8221; + onlineCounter.getCount());</p>
<p>onlineCounter.decCount();</p>
<p>Console.WriteLine(&#8220;在线人数：&#8221; + onlineCounter.getCount());</p>
<p>}</p>
<p>}</p>
<p><a href="http://www.iwanna.cn/tags/php/" class="st_tag internal_tag" rel="tag nofollow" title="Posts tagged with PHP">Php</a>代码：</p>
<p>//在线人数计数器</p>
<p>class OnlineCounter {</p>
<p>//在线人数</p>
<p>private $onlineCount = 0;</p>
<p>//构造函数</p>
<p>public function __construct(){</p>
<p>//从文件或者数据库读取数据,假如读出来的数据是100</p>
<p>$this-&gt;onlineCount = 100;</p>
<p>}</p>
<p>//在用户登录后，在线人数加1</p>
<p>public function incCount(){</p>
<p>$this-&gt;onlineCount++;</p>
<p>}</p>
<p>//在用户退出后，在线人数减1</p>
<p>public function decCount(){</p>
<p>$this-&gt;onlineCount&#8211;;</p>
<p>}</p>
<p>//保存在线人数</p>
<p>public function saveCount(){</p>
<p>}</p>
<p>//获取在线人数</p>
<p>public function getCount(){</p>
<p>return $this-&gt;onlineCount;</p>
<p>}</p>
<p>//测试函数</p>
<p>public static function execute() {</p>
<p>$onlineCounter = new OnlineCounter();</p>
<p>echo &#8220;在线人数：&#8221; . $onlineCounter-&gt;getCount();</p>
<p>$onlineCounter-&gt;incCount();</p>
<p>echo &#8220;在线人数：&#8221; . $onlineCounter-&gt;getCount();</p>
<p>$onlineCounter-&gt;decCount();</p>
<p>echo &#8220;在线人数：&#8221; . $onlineCounter-&gt;getCount();</p>
<p>}</p>
<p>}</p>
<p>OnlineCounter::execute();</p>
<p>?&gt;</p>
<p>运行结果如下：</p>
<p>在线人数：100</p>
<p>在线人数：101</p>
<p>在线人数：100</p>
<p>网站代码中凡是用到计数器的地方，只要new一个计数器对象，然后就可以获取、保存、增加或者减少在线人数的数量。不过，我们的代码实际的使用效果并不好。假如有多个用户同时登录,那么在这个时刻，通过计数器取到的在线人数是相同的，于是他们使用各自的计数器加1后存入文件或者数据库。这样操作后续登陆的用户得到的在线人数，与实际的在线人数并不一致。所以，把这个计数器设计为一个全局对象，所有人都共用同一份数据，就可以避免类似的问题，这就是我们所说的单例模式的其中的一种应用。<br />
2         什么是单例模式</p>
<p>单例模式能够保证一个类仅有唯一的实例，并提供一个全局访问点。</p>
<p>我们是不是可以通过一个全局变量来实现单例模式的要求呢？我们只要仔细地想想看，全局变量确实可以提供一个全局访问点，但是它不能防止别人实例化多个对象。通过外部程序来控制的对象的产生的个数，势必会系统的增加管理成本，增大模块之间的耦合度。所以，最好的解决办法就是让类自己负责保存它的唯一实例，并且让这个类保证不会产生第二个实例，同时提供一个让外部对象访问该实例的方法。自己的事情自己办，而不是由别人代办，这非常符合面向对象的封装原则。</p>
<p>按照以上的思路，我们可以这样来设计单例类：</p>
<p><a href="http://www.iwanna.cn/tags/java/" class="st_tag internal_tag" rel="tag nofollow" title="Posts tagged with Java">Java</a>代码:</p>
<p>class Singleton {</p>
<p>// 私有的静态对象</p>
<p>private static Singleton instance = null;</p>
<p>//私有的构造方法</p>
<p>private Singleton (){</p>
<p>}</p>
<p>// 公开的静态工厂方法,返回此类的唯一实例</p>
<p>public static Singleton getInstance(){</p>
<p>if(instance == null){</p>
<p>instance = new Singleton();</p>
<p>}</p>
<p>return instance;</p>
<p>}</p>
<p>}</p>
<p>.Net代码：</p>
<p>class Singleton{</p>
<p>// 私有的静态对象</p>
<p>private static Singleton instance = null;</p>
<p>//私有的构造方法</p>
<p>private Singleton(){</p>
<p>}</p>
<p>//公开的静态工厂方法,返回此类的唯一实例</p>
<p>public static Singleton getInstance(){</p>
<p>if (instance == null){</p>
<p>instance = new Singleton();</p>
<p>}</p>
<p>return instance;</p>
<p>}</p>
<p>}</p>
<p><a href="http://www.iwanna.cn/tags/php/" class="st_tag internal_tag" rel="tag nofollow" title="Posts tagged with PHP">Php</a>代码：</p>
<p>class Singleton {</p>
<p>// 私有的静态对象</p>
<p>private static $instance = null;</p>
<p>//私有的构造方法</p>
<p>private function __construct(){</p>
<p>}</p>
<p>// 公开的静态工厂方法,返回此类的唯一实例</p>
<p>public static function getInstance(){</p>
<p>if(self::$instance == null){</p>
<p>self::$instance = new Singleton();</p>
<p>}</p>
<p>return self::$instance;</p>
<p>}</p>
<p>}</p>
<p>?&gt;</p>
<p>Singleton类含有一个instance的私有静态变量,用来保存该类唯一的实例对象，它对于外部对象是不可见的，只能通过getInstance方法才能获得。</p>
<p>Singleton类的构造器是private私有的，外部对象无法通过它的构造器生成实例，也就是说外部程序试图通过new操作符来创建实例是行不通的，因此，getInstance方法成为获得Singleton类实例的唯一途径。</p>
<p>getInstance方法的设计非常简单，它首先检测instance变量是否已经初始化，如果没有被初始化，就创建一个实例保存到instance变量，最后返回这个实例；如果这个实例已经被初始化，那么就直接返回这个实例。</p>
<p>getInstance方法的设计非常简单，它首先检测instance变量是否已经初始化，如果没有被初始化，就创建一个实例保存到instance变量，最后返回这个实例；如果这个实例已经被初始化，那么就直接返回这个实例。</p>
<p>单例模式的类图</p>
<p>单例模式主要有3个特点，：</p>
<p>1、单例类确保自己只有一个实例。</p>
<p>2、单例类必须自己创建自己的实例。</p>
<p>3、单例类必须为其他对象提供唯一的实例。<br />
3         安全的单例模式：双重检查锁定机制</p>
<p>我们虽然实现了单例模式，但是目前的解决办法并不安全，依然存在着一定的缺陷：</p>
<p>class Singleton {</p>
<p>private static Singleton instance = null;</p>
<p>private Singleton (){</p>
<p>}</p>
<p>public static Singleton getInstance(){</p>
<p>if(instance == null){</p>
<p>instance = new Singleton();</p>
<p>}</p>
<p>return instance;</p>
<p>}</p>
<p>}</p>
<p>这段代码意图通过检查if (instance == null)这个条件，来保证只创建一个Singleton实例。事实上，它运行在单线程环境中，得到的结果是正确的，没有问题，但是运行在多线程的环境中，它就会出现错误，可能有多个Singleton实例被创建出来。</p>
<p>我们分析一下，在多线程环境中，可能会出现这样的情形：线程A 和线程B几乎同时到达if (instance == null)语句，假设线程A 比线程B 早一点点，那么：</p>
<p>（1）A 会首先进入if (instance == null)块的内部，并开始执行new Singleton () 语句。此时，instance变量仍然是null，直到线程A 的new Singleton () 语句返回，并给instance变量赋值为止。</p>
<p>（2） 但是，此时的线程B 并不会在if (instance == null)语句的外面等待，因为此时(instance == null)是成立的，它会马上进入if (instance == null)语句块的内部。这样，线程B 会不可避免地执行instance=new Singleton()语句，从而创建出第二个实例来。</p>
<p>（3）线程A 的instance=new Singleton()语句执行完毕后，instance变量得到了真实的对象引用，(instance == null)不再为真。所以，后来的线程就不会再进入if (instance == null) 语句块的内部了。</p>
<p>（4）线程B 的instance=new Singleton()语句也执行完毕后，instance变量的值被覆盖。但是第一个instance对象被线程A 引用的事实已经无法改变了。这时候的线程A和B各自拥有一个独立的Singleton对象。</p>
<p>为了实现线程安全，我们对代码进行了一定的改造：</p>
<p><a href="http://www.iwanna.cn/tags/java/" class="st_tag internal_tag" rel="tag nofollow" title="Posts tagged with Java">Java</a>代码：</p>
<p>class Singleton{</p>
<p>private static Singleton instance = null;</p>
<p>private Singleton() {</p>
<p>}</p>
<p>public static Singleton getInstance(){</p>
<p>//位置1，第1次检查instance</p>
<p>if (instance == null)</p>
<p>{</p>
<p>//位置2，某一时刻可能有n个线程到达</p>
<p>synchronized (this)</p>
<p>{</p>
<p>//位置3，任何时间只能有1个线程到达</p>
<p>if (instance == null) //位置4，第2次检查instance</p>
<p>{</p>
<p>instance = new Singleton();</p>
<p>}</p>
<p>}</p>
<p>}</p>
<p>return instance;</p>
<p>}</p>
<p>}</p>
<p>.Net代码：</p>
<p>class Singleton {</p>
<p>private static Singleton instance = null;</p>
<p>private static readonly object syncObj = new object();</p>
<p>private Singleton (){</p>
<p>}</p>
<p>public static Singleton getInstance(){</p>
<p>//位置1，第1次检查instance</p>
<p>if( instance == null ){</p>
<p>//位置2，某一时刻可能有n个线程到达</p>
<p>lock(syncObj)</p>
<p>{</p>
<p>//位置3，任何时间只能有1个线程到达</p>
<p>if (instance == null) //位置4，第2次检查instance</p>
<p>{</p>
<p>instance = new Singleton();</p>
<p>}</p>
<p>}</p>
<p>}</p>
<p>return instance;</p>
<p>}</p>
<p>}</p>
<p>我们通过引入了<a href="http://www.iwanna.cn/tags/java/" class="st_tag internal_tag" rel="tag nofollow" title="Posts tagged with Java">Java</a>的synchronized或者.Net的lock同步化限制，各个线程到达临界区时，就会按照线性方式逐个执行。</p>
<p>我们再来分析一下：</p>
<p>（1）在多线程环境中，线程A 和B同时或几乎同时到达位置1。</p>
<p>（2）假设线程A 会首先到达位置2，并进入synchronized(this) 到达位置3。这时，由于synchronized(this) 的同步化限制，线程B 无法到达位置3，而只能在位置2 等候。</p>
<p>（3）线程A 执行instance = new Singleton()语句，instance变量得到赋值，此时，线程B 还只能继续在位置2 等候。</p>
<p>（4）线程A 退出synchronized(this) 块，并返回instance对象。</p>
<p>（5）线程B 进入synchronized(this)块，到达位置3，进而到达位置4。由于instance变量已经不是null 了，因此线程B 退出synchronized(this)，并返回instance，这时候的instance只有一个。</p>
<p>我们通过两次检查instance是否被实例化来解决线程安全问题，这种处理方式称为双重检查锁定机制（Double-checked locking）。还有另外一种解决线程安全的方法，就是把getInstance方法整体作为同步区，比如声明为public static synchronized Singleton getInstance()，这种方式由于锁定的区域过大，特殊情况下会造成系统性能的下降，成为系统的性能瓶颈。</p>
<p>双重检查锁定机制不仅解决了线程安全问题，而且把性能也处理得很不错，看起来非常完美。不幸的是我们应该注意不要在<a href="http://www.iwanna.cn/tags/java/" class="st_tag internal_tag" rel="tag nofollow" title="Posts tagged with Java">java</a>中使用双重检查锁定机制，由于<a href="http://www.iwanna.cn/tags/java/" class="st_tag internal_tag" rel="tag nofollow" title="Posts tagged with Java">Java</a>编译器和 JIT 的优化的原因，系统无法保证我们期望的执行次序。虽然<a href="http://www.iwanna.cn/tags/java/" class="st_tag internal_tag" rel="tag nofollow" title="Posts tagged with Java">Java</a>语法中的volatile修饰符可以强制屏蔽编译器和 JIT 的优化工作，但它是一种非常脆弱的同步机制，比较难以控制，所以建议尽量减少使用。我们后面还提供了其它的一种实现方式。<br />
4         单例模式的实现方式：懒汉单例类和饿汉单例类</p>
<p>单例模式的实现有多种方法，常见的就有懒汉式单例类和饿汉式单例类。我们前面介绍的实现方法就属于懒汉式单例类。</p>
<p>l         懒汉式单例类</p>
<p>对于懒汉模式，我们可以这样理解：该单例类非常懒，只有在自身需要的时候才会行动，从来不知道及早做好准备。它在需要对象的时候，才判断是否已有对象，如果没有就立即创建一个对象，然后返回，如果已有对象就不再创建，立即返回。</p>
<p>懒汉模式只在外部对象第一次请求实例的时候才去创建。</p>
<p>l         饿汉式单例</p>
<p>对于饿汉模式，我们可以这样理解：该单例类非常饿，迫切需要吃东西，所以它在类加载的时候就立即创建对象。</p>
<p><a href="http://www.iwanna.cn/tags/java/" class="st_tag internal_tag" rel="tag nofollow" title="Posts tagged with Java">Java</a>代码：</p>
<p>final class Singleton {</p>
<p>//私有的唯一实例成员,在类加载的时候就创建好了单例对象</p>
<p>private static final Singleton instance = new Singleton();</p>
<p>//私有的构造方法,避免外部创建类实例</p>
<p>private Singleton() {</p>
<p>}</p>
<p>//静态工厂方法,返回此类的唯一实例</p>
<p>public static Singleton getInstance() {</p>
<p>return instance;</p>
<p>}</p>
<p>}</p>
<p>.Net代码：</p>
<p>sealed class Singleton {</p>
<p>//私有的唯一实例成员,在类加载的时候就创建好了单例对象</p>
<p>private static readonly Singleton instance = new Singleton();</p>
<p>//私有的构造方法,避免外部创建类实例</p>
<p>private Singleton() {</p>
<p>}</p>
<p>//静态工厂方法,返回此类的唯一实例</p>
<p>public static Singleton getInstance() {</p>
<p>return instance;</p>
<p>}</p>
<p>}</p>
<p>使用<a href="http://www.iwanna.cn/tags/java/" class="st_tag internal_tag" rel="tag nofollow" title="Posts tagged with Java">Java</a>中的final关键字和.Net中sealed关键字去修饰class，目的是阻止派生子类，而派生子类可能会导致实例不唯一。使用<a href="http://www.iwanna.cn/tags/java/" class="st_tag internal_tag" rel="tag nofollow" title="Posts tagged with Java">Java</a>中的final关键字和.Net中readonly关键字去修饰变量，就意味着只能在类初始化时或者在构造器中分配该变量。</p>
<p>我们对比一下懒汉模式和饿汉模式的优缺点：</p>
<p>懒汉模式，它的特点是运行时获得对象的速度比较慢，但加载类的时候比较快。它在整个应用的生命周期只有一部分时间在占用资源。</p>
<p>饿汉模式，它的特点是加载类的时候比较慢，但运行时获得对象的速度比较快。它从加载到应用结束会一直占用资源。</p>
<p>这两种模式对于初始化较快，占用资源少的对象来说，没有多大的性能差异，但是对于初始化慢，占用资源多的对象来说就会有比较明显的差别了。所以，对重量级对象应用饿汉模式，在类加载时需要较长时间，但运行时会有明显的时间效率的提升。对重量级对象应用懒汉模式，在类加载时很快，但至少第一次获得对象时需要等待很长时间。</p>
<p>从用户体验的角度来说，我们应该首选饿汉模式。我们愿意等待某个程序花较长的时间初始化，却不喜欢在程序运行时等待太久，给人一种反应迟钝的感觉，所以对于有重量级对象参与的单例模式，我们推荐使用饿汉模式。</p>
<p>而对于初始化较快的轻量级对象来说，选用哪种方法都可以。如果一个应用中使用了大量单例模式，我们就应该权衡两种方法了。轻量级对象的单例采用懒汉模式，减轻加载时的负担，缩短加载时间，提高加载效率；同时由于是轻量级对象，把这些对象的创建放在使用时进行，实际就是把创建单例对象所消耗的时间分摊到整个应用中去了，对于整个应用的运行效率没有太大影响。<br />
5         什么情况下使用单例模式</p>
<p>单例模式也是一种比较常见的设计模式，它到底能带给我们什么好处呢？其实无非是三个方面的作用：</p>
<p>第一、控制资源的使用，通过线程同步来控制资源的并发访问；</p>
<p>第二、控制实例产生的数量，达到节约资源的目的。</p>
<p>第三、作为通信媒介使用，也就是数据共享，它可以在不建立直接关联的条件下，让多个不相关的两个线程或者进程之间实现通信。</p>
<p>比如，数据库连接池的设计一般采用单例模式，数据库连接是一种数据库资源。软件系统中使用数据库连接池，主要是节省打开或者关闭数据库连接所引起的效率损耗，这种效率上的损耗还是非常昂贵的。当然，使用数据库连接池还有很多其它的好处，可以屏蔽不同数据数据库之间的差异，实现系统对数据库的低度耦合，也可以被多个系统同时使用，具有高可复用性，还能方便对数据库连接的管理等等。数据库连接池属于重量级资源，一个应用中只需要保留一份即可，既节省了资源又方便管理。所以数据库连接池采用单例模式进行设计会是一个非常好的选择。</p>
<p>在我们日常使用的在Windows中也有不少单例模式设计的组件，象常用的文件管理器。由于Windows操作系统是一个典型的多进程多线程系统，那么在创建或者删除某个文件的时候，就不可避免地出现多个进程或线程同时操作一个文件的现象。采用单例模式设计的文件管理器就可以完美的解决这个问题，所有的文件操作都必须通过唯一的实例进行，这样就不会产生混乱的现象。</p>
<p>再比如，每台计算机可以有若干个打印机，如果每一个进程或者线程都独立地使用打印机资源的话，那么我们打印出来的结果就有可能既包含这个打印任务的一部分，又包含另外一个打印任务的一部分。所以，大多数的操作系统最终为打印任务设计了一个单例模式的假脱机服务Printer Spooler，所有的打印任务都需要通过假脱机服务进行。</p>
<p>实际上，配置信息类、管理类、控制类、门面类、代理类通常被设计为单例类。像<a href="http://www.iwanna.cn/tags/java/" class="st_tag internal_tag" rel="tag nofollow" title="Posts tagged with Java">Java</a>的Struts、Spring框架，.Net的Spring<a href="http://www.iwanna.cn/tags/net/" class="st_tag internal_tag" rel="tag nofollow" title="Posts tagged with .Net">.Net</a>框架，以及<a href="http://www.iwanna.cn/tags/php/" class="st_tag internal_tag" rel="tag nofollow" title="Posts tagged with PHP">Php</a>的Zend框架都大量使用了单例模式。</p>
<hr />
<p>© <a href="http://www.iwanna.cn">我想网</a> Akon 所有 , 2009. |
<a href="http://www.iwanna.cn/archives/2009/04/02/149/">永久链接</a> |
<a href="http://www.iwanna.cn/archives/2009/04/02/149/#comments">没有评论</a> |
提交到
<a rel="nofollow" target="_blank" href="http://www.google.com/reader/view/feed/http://www.iwanna.cn/archives/2009/04/02/149/">Google Reader</a>
<a rel="nofollow" target="_blank" href="http://www.xianguo.com/subscribe.php?url=http://www.iwanna.cn/archives/2009/04/02/149/">鲜果</a>
<a rel="nofollow" target="_blank" href="http://www.zhuaxia.com/add_channel.php?url=http://www.iwanna.cn/archives/2009/04/02/149/">抓虾</a>
<hr />
</p>
	标签：<a href="http://www.iwanna.cn/topics/develope/net/" title=".Net" rel="tag nofollow">.Net</a>, <a href="http://www.iwanna.cn/tags/net/" title=".Net" rel="tag nofollow">.Net</a>, <a href="http://www.iwanna.cn/topics/develope/java/" title="Java" rel="tag nofollow">Java</a>, <a href="http://www.iwanna.cn/tags/java/" title="Java" rel="tag nofollow">Java</a>, <a href="http://www.iwanna.cn/topics/develope/patten/" title="Patten" rel="tag nofollow">Patten</a>, <a href="http://www.iwanna.cn/tags/patten/" title="Patten" rel="tag nofollow">Patten</a>, <a href="http://www.iwanna.cn/topics/develope/php/" title="PHP" rel="tag nofollow">PHP</a>, <a href="http://www.iwanna.cn/tags/php/" title="PHP" rel="tag nofollow">PHP</a>, <a href="http://www.iwanna.cn/topics/iwanna/" title="我想想想" rel="tag nofollow">我想想想</a><br />

	<h2 class="related_post">您可能会感兴趣的其他文章</h2>
	<ul class="st-related-posts">
	<li><a href="http://www.iwanna.cn/archives/2009/08/22/2181/" title="PHP开发者有了通往.net的新桥梁 (2009年08月22日)">PHP开发者有了通往.net的新桥梁</a> </li>
	<li><a href="http://www.iwanna.cn/archives/2010/06/12/3907/" title="适配器模式 (2010年06月12日)">适配器模式</a> </li>
	<li><a href="http://www.iwanna.cn/archives/2009/07/31/2088/" title="论述PHP开发框架: What, When, Why and Which? (2009年07月31日)">论述PHP开发框架: What, When, Why and Which?</a> </li>
	<li><a href="http://www.iwanna.cn/archives/2010/06/08/3767/" title="腾讯PHP程序员面试题目 (2010年06月8日)">腾讯PHP程序员面试题目</a> </li>
	<li><a href="http://www.iwanna.cn/archives/2010/07/05/4365/" title="类的设计原则 (2010年07月5日)">类的设计原则</a> </li>
	<li><a href="http://www.iwanna.cn/archives/2009/04/03/171/" title="简易Wordpress模板代码帮助手册中文版 (2009年04月3日)">简易Wordpress模板代码帮助手册中文版</a> </li>
	<li><a href="http://www.iwanna.cn/archives/2010/06/08/3769/" title="用PHP数组对百万数据进行排重 (2010年06月8日)">用PHP数组对百万数据进行排重</a> </li>
	<li><a href="http://www.iwanna.cn/archives/2009/07/05/1933/" title="用 PHP 读取和编写 XML DOM (2009年07月5日)">用 PHP 读取和编写 XML DOM</a> </li>
	<li><a href="http://www.iwanna.cn/archives/2010/06/20/4102/" title="深入理解PHP之匿名函数 (2010年06月20日)">深入理解PHP之匿名函数</a> </li>
	<li><a href="http://www.iwanna.cn/archives/2009/08/20/2171/" title="最快的 PHP 模板引擎 Blitz Templates (2009年08月20日)">最快的 PHP 模板引擎 Blitz Templates</a> </li>
</ul>


<p><small>Feed enhanced by <a href='http://planetozh.com/blog/my-projects/wordpress-plugin-better-feed-rss/'>Better Feed</a> from  <a href='http://planetozh.com/blog/'>Ozh</a></small></p>
]]></content:encoded>
			<wfw:commentRss>http://www.iwanna.cn/archives/2009/04/02/149/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>JavaScript事件设计模式</title>
		<link>http://www.iwanna.cn/archives/2009/04/01/134/</link>
		<comments>http://www.iwanna.cn/archives/2009/04/01/134/#comments</comments>
		<pubDate>Wed, 01 Apr 2009 04:56:42 +0000</pubDate>
		<dc:creator>seasun</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Patten]]></category>

		<guid isPermaLink="false">http://www.iwanna.cn/?p=134</guid>
		<description><![CDATA[本文章参考自：《征服Ajax Web 2.0 开发技术详解》为了自己日后查阅并与大家共享。
1. 事件设计概述
 事件机制可以是程序逻辑更加清晰可见，在JavaScript中很多对象都有自己的事件，如：button有onclick事件，selcet有 onchange事件。对于我们自己设计的类，是否也可以有事件机制呢？答案是肯定的。我们可以通过事件机制，将类设计为独立的模块，从而使其可以通过事 件与外通信，提高程序的开发效率。
2. 不带参数的事件设计模式
 最简单的一种模式是将一个类的方法成员定义为事件，可以借助JavaScript的基本语法来实现，通常是一个空的方法。例如：


Js代码 


&#60;script language=&#8220;javascript&#8221; type=&#8220;text/javascript&#8221; &#62; 
 
function User(){ 
} 
User.prototype={ 
 show:function(){ 
 this.onShow();//触发onShow事件 
 }, 
 onShow:function(){}//定义事件接口 
} 
var obj = new User(); 
//创建obj的onShow事件处理程序 
obj.onShow = function(){ 
 alert(&#8220;事件触发了&#8221;); 
} 
//调用obj的show方法 
obj.show(); 
&#60;/script&#62; 


&#60;script language="javascript" type="text/javascript" &#62;

function User(){
}
User.prototype={
	show:function(){
		this.onShow();//触发onShow事件
	},
	onShow:function(){}//定义事件接口
}
var obj = new User();
//创建obj的onShow事件处理程序
obj.onShow = function(){
	alert("事件触发了");
}
//调用obj的show方法
obj.show();
&#60;/script&#62;
  [...]]]></description>
			<content:encoded><![CDATA[<p><span>本文章参考自：《征服Ajax Web 2.0 开发技术详解》为了自己日后查阅并与大家共享。</span></p>
<p><span>1. 事件设计概述</span></p>
<p><span> 事件机制可以是程序逻辑更加清晰可见，在<a href="http://www.iwanna.cn/tags/javascript/" class="st_tag internal_tag" rel="tag nofollow" title="Posts tagged with JavaScript">JavaScript</a>中很多对象都有自己的事件，如：button有onclick事件，selcet有 onchange事件。对于我们自己设计的类，是否也可以有事件机制呢？答案是肯定的。我们可以通过事件机制，将类设计为独立的模块，从而使其可以通过事 件与外通信，提高程序的开发效率。</span></p>
<p><span>2. 不带参数的事件设计模式</span></p>
<p><span> <span id="more-134"></span>最简单的一种模式是将一个类的方法成员定义为事件，可以借助<a href="http://www.iwanna.cn/tags/javascript/" class="st_tag internal_tag" rel="tag nofollow" title="Posts tagged with JavaScript">JavaScript</a>的基本语法来实现，通常是一个空的方法。例如：</span></p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Js代码 <object width="14" height="15" data="http://plkong.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf" type="application/x-shockwave-flash"><param name="flashvars" value="clipboard=%3Cscript%20language%3D%22javascript%22%20type%3D%22text%2Fjavascript%22%20%3E%0A%0Afunction%20User()%7B%0A%7D%0AUser.prototype%3D%7B%0A%09show%3Afunction()%7B%0A%09%09this.onShow()%3B%2F%2F%E8%A7%A6%E5%8F%91onShow%E4%BA%8B%E4%BB%B6%0A%09%7D%2C%0A%09onShow%3Afunction()%7B%7D%2F%2F%E5%AE%9A%E4%B9%89%E4%BA%8B%E4%BB%B6%E6%8E%A5%E5%8F%A3%0A%7D%0Avar%20obj%20%3D%20new%20User()%3B%0A%2F%2F%E5%88%9B%E5%BB%BAobj%E7%9A%84onShow%E4%BA%8B%E4%BB%B6%E5%A4%84%E7%90%86%E7%A8%8B%E5%BA%8F%0Aobj.onShow%20%3D%20function()%7B%0A%09alert(%22%E4%BA%8B%E4%BB%B6%E8%A7%A6%E5%8F%91%E4%BA%86%22)%3B%0A%7D%0A%2F%2F%E8%B0%83%E7%94%A8obj%E7%9A%84show%E6%96%B9%E6%B3%95%0Aobj.show()%3B%0A%3C%2Fscript%3E" /><param name="src" value="http://plkong.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf" /><param name="quality" value="high" /></object></div>
</div>
<ol class="dp-c">
<li><span><span>&lt;script language=</span><span class="string">&#8220;<a href="http://www.iwanna.cn/tags/javascript/" class="st_tag internal_tag" rel="tag nofollow" title="Posts tagged with JavaScript">javascript</a>&#8221;</span><span> type=</span><span class="string">&#8220;text/<a href="http://www.iwanna.cn/tags/javascript/" class="st_tag internal_tag" rel="tag nofollow" title="Posts tagged with JavaScript">javascript</a>&#8221;</span><span> &gt; </span></span></li>
<li><span> </span></li>
<li><span><span class="keyword">function</span><span> User(){ </span></span></li>
<li><span>} </span></li>
<li><span>User.prototype={ </span></li>
<li><span> show:<span class="keyword">function</span><span>(){ </span></span></li>
<li><span> <span class="keyword">this</span><span>.onShow();</span><span class="comment">//触发onShow事件</span><span> </span></span></li>
<li><span> }, </span></li>
<li><span> onShow:<span class="keyword">function</span><span>(){}</span><span class="comment">//定义事件接口</span><span> </span></span></li>
<li><span>} </span></li>
<li><span><span class="keyword">var</span><span> obj = </span><span class="keyword">new</span><span> User(); </span></span></li>
<li><span><span class="comment">//创建obj的onShow事件处理程序</span><span> </span></span></li>
<li><span>obj.onShow = <span class="keyword">function</span><span>(){ </span></span></li>
<li><span> alert(<span class="string">&#8220;事件触发了&#8221;</span><span>); </span></span></li>
<li><span>} </span></li>
<li><span><span class="comment">//调用obj的show方法</span><span> </span></span></li>
<li><span>obj.show(); </span></li>
<li><span>&lt;/script&gt; </span></li>
</ol>
</div>
<pre class="js" style="display: none;">&lt;script language="<a href="http://www.iwanna.cn/tags/javascript/" class="st_tag internal_tag" rel="tag nofollow" title="Posts tagged with JavaScript">javascript</a>" type="text/<a href="http://www.iwanna.cn/tags/javascript/" class="st_tag internal_tag" rel="tag nofollow" title="Posts tagged with JavaScript">javascript</a>" &gt;

function User(){
}
User.prototype={
	show:function(){
		this.onShow();//触发onShow事件
	},
	onShow:function(){}//定义事件接口
}
var obj = new User();
//创建obj的onShow事件处理程序
obj.onShow = function(){
	alert("事件触发了");
}
//调用obj的show方法
obj.show();
&lt;/script&gt;</pre>
<p><span> </span> <span> </span> <span> </span> <span> </span> <span> </span> <span style="color: #ff0000; font-size: small;"> obj.onShow</span> <span> </span> <span> </span> <span> 方法在类的外部被定义，在类的内部方法</span> <span> </span> <span> </span> <span style="color: #ff0000; font-size: small;">show()</span> <span> </span> <span> </span> <span>中被调用，这就实现了事件机制。</span></p>
<p><span> 此设计模式应用起来简单，但有其有以下缺点：</span></p>
<ul>
<li><span>不能够给事件处理程序传递参数，原因是我们是在</span> <span> </span> <span> </span> <span style="color: #ff0000; font-size: small;">show()</span> <span> </span> <span> </span> <span>这个内部方法中调用事件处理程序的，无法知道外部的参数。</span></li>
<li><span>每个事件接口只能绑定一个事件处理程序，而内部方法则可以使用</span> <span> </span> <span style="color: #ff0000; font-size: small;">attachEvent</span> <span> </span> <span> </span> <span>或</span> <span> </span> <span> </span> <span style="color: #ff0000; font-size: small;">addEventListener</span> <span> </span> <span>方法绑定多个处理程序。</span></li>
</ul>
<p><span>3. 给事件处理程序传递参数</span></p>
<p><span> 给事件处理程序传递参数不仅是自定义事件中存在的问题，也是系统内部对象的事件机制中存在的问题，因为事件机制仅传递一个函数名称，不带有任何参数信息，所以无法传递参数进去。例如：</span></p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Js代码 <object width="14" height="15" data="http://plkong.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf" type="application/x-shockwave-flash"><param name="flashvars" value="clipboard=%3Cscript%20language%3D%22javascript%22%20type%3D%22text%2Fjavascript%22%20%3E%0Afunction%20User()%7B%0A%7D%0AUser.prototype%3D%7B%0A%09show%3Afunction()%7B%0A%09%09this.onShow()%3B%2F%2F%E8%A7%A6%E5%8F%91onShow%E4%BA%8B%E4%BB%B6%0A%09%7D%2C%0A%09onShow%3Afunction()%7B%7D%2F%2F%E5%AE%9A%E4%B9%89%E4%BA%8B%E4%BB%B6%E6%8E%A5%E5%8F%A3%0A%7D%0Avar%20obj%20%3D%20new%20User()%3B%0A%2F%2F%E5%88%9B%E5%BB%BAobj%E7%9A%84onshow%E4%BA%8B%E4%BB%B6%E5%A4%84%E7%90%86%E7%A8%8B%E5%BA%8F%0Afunction%20objOnShow(userName)%7B%0A%09alert(%22hello%2C%22%2BuserName)%3B%0A%7D%0A%2F%2F%E5%AE%9A%E4%B9%89username%E5%8F%98%E9%87%8F%0Avar%20userName%20%3D%20%22plkong%22%3B%0A%2F%2F%E7%BB%91%E5%AE%9Aobj%E7%9A%84onshow%E4%BA%8B%E4%BB%B6%0Aobj.onShow%20%3D%20objOnShow%3B%2F%2F%E6%97%A0%E6%B3%95%E5%B0%86userName%E8%BF%99%E4%B8%AA%E5%8F%98%E9%87%8F%E4%BC%A0%E9%80%92%E8%BF%9B%E5%8E%BB%0Aobj.show()%3B%0A%3C%2Fscript%3E" /><param name="src" value="http://plkong.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf" /><param name="quality" value="high" /></object></div>
</div>
<ol class="dp-c">
<li><span><span>&lt;script language=</span><span class="string">&#8220;<a href="http://www.iwanna.cn/tags/javascript/" class="st_tag internal_tag" rel="tag nofollow" title="Posts tagged with JavaScript">javascript</a>&#8221;</span><span> type=</span><span class="string">&#8220;text/<a href="http://www.iwanna.cn/tags/javascript/" class="st_tag internal_tag" rel="tag nofollow" title="Posts tagged with JavaScript">javascript</a>&#8221;</span><span> &gt; </span></span></li>
<li><span><span class="keyword">function</span><span> User(){ </span></span></li>
<li><span>} </span></li>
<li><span>User.prototype={ </span></li>
<li><span> show:<span class="keyword">function</span><span>(){ </span></span></li>
<li><span> <span class="keyword">this</span><span>.onShow();</span><span class="comment">//触发onShow事件</span><span> </span></span></li>
<li><span> }, </span></li>
<li><span> onShow:<span class="keyword">function</span><span>(){}</span><span class="comment">//定义事件接口</span><span> </span></span></li>
<li><span>} </span></li>
<li><span><span class="keyword">var</span><span> obj = </span><span class="keyword">new</span><span> User(); </span></span></li>
<li><span><span class="comment">//创建obj的onshow事件处理程序</span><span> </span></span></li>
<li><span><span class="keyword">function</span><span> objOnShow(userName){ </span></span></li>
<li><span> alert(<span class="string">&#8220;hello,&#8221;</span><span>+userName); </span></span></li>
<li><span>} </span></li>
<li><span><span class="comment">//定义username变量</span><span> </span></span></li>
<li><span><span class="keyword">var</span><span> userName = </span><span class="string">&#8220;plkong&#8221;</span><span>; </span></span></li>
<li><span><span class="comment">//绑定obj的onshow事件</span><span> </span></span></li>
<li><span>obj.onShow = objOnShow;<span class="comment">//无法将userName这个变量传递进去</span><span> </span></span></li>
<li><span>obj.show(); </span></li>
<li><span>&lt;/script&gt; </span></li>
</ol>
</div>
<pre class="js" style="display: none;">&lt;script language="<a href="http://www.iwanna.cn/tags/javascript/" class="st_tag internal_tag" rel="tag nofollow" title="Posts tagged with JavaScript">javascript</a>" type="text/<a href="http://www.iwanna.cn/tags/javascript/" class="st_tag internal_tag" rel="tag nofollow" title="Posts tagged with JavaScript">javascript</a>" &gt;
function User(){
}
User.prototype={
	show:function(){
		this.onShow();//触发onShow事件
	},
	onShow:function(){}//定义事件接口
}
var obj = new User();
//创建obj的onshow事件处理程序
function objOnShow(userName){
	alert("hello,"+userName);
}
//定义username变量
var userName = "plkong";
//绑定obj的onshow事件
obj.onShow = objOnShow;//无法将userName这个变量传递进去
obj.show();
&lt;/script&gt;</pre>
<p><span> 上面的程序是无法传递参数进去的，为了解决这个问题，我们可以从相反的思维方式去考虑问题。不考虑怎么把参数传递进去，而是考虑如何构建一个无需参数的事件处理程序。我们看看先看看下面的函数：</span></p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Js代码 <object width="14" height="15" data="http://plkong.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf" type="application/x-shockwave-flash"><param name="flashvars" value="clipboard=%2F*%20%E5%B0%86%E5%8F%82%E6%95%B0%E7%9A%84%E5%87%BD%E6%95%B0%E5%B0%81%E8%A3%85%E4%B8%BA%E6%97%A0%E5%8F%82%E6%95%B0%E7%9A%84%E5%87%BD%E6%95%B0%20*%2F%0Afunction%20createFunction(obj%2C%20strFunc)%7B%0A%09var%20args%20%3D%20%5B%5D%3B%2F%2F%E5%AE%9A%E4%B9%89args%E7%94%A8%E4%BA%8E%E5%AD%98%E5%82%A8%E4%BC%A0%E9%80%92%E7%BB%99%E4%BA%8B%E4%BB%B6%E5%A4%84%E7%90%86%E7%A8%8B%E5%BA%8F%E7%9A%84%E5%8F%82%E6%95%B0%0A%09if(!obj)%20obj%20%3D%20window%3B%2F%2F%E5%A6%82%E6%9E%9C%E6%98%AF%E5%85%A8%E5%B1%80%E5%87%BD%E6%95%B0%E5%88%99obj%20%3D%20window%3B%0A%09%2F%2F%E5%BE%97%E5%88%B0%E4%BC%A0%E9%80%92%E7%BB%99%E4%BA%8B%E4%BB%B6%E5%A4%84%E7%90%86%E7%A8%8B%E5%BA%8F%E7%9A%84%E5%8F%82%E6%95%B0%0A%09for(%20var%20i%20%3D%202%3B%20i%3Carguments.length%3B%20i%2B%2B)%0A%09%09args.push(arguments%5Bi%5D)%3B%0A%09%2F%2F%E7%94%A8%E6%97%A0%E5%8F%82%E5%87%BD%E6%95%B0%E5%B0%81%E8%A3%85%E4%BA%8B%E4%BB%B6%E5%A4%84%E7%90%86%E7%A8%8B%E5%BA%8F%E7%9A%84%E8%B0%83%E7%94%A8%0A%09%2F*%0A%09JavaScript%E4%B8%BA%E5%87%BD%E6%95%B0%E5%AF%B9%E8%B1%A1%E5%AE%9A%E4%B9%89%E4%BA%86%E4%B8%A4%E4%B8%AA%E6%96%B9%E6%B3%95%EF%BC%9Aapply%20%E5%92%8C%20call%EF%BC%8C%20%E5%AE%83%E4%BB%AC%E7%9A%84%E4%BD%9C%E7%94%A8%E9%83%BD%E6%98%AF%E5%87%BD%E6%95%B0%E7%BB%91%E5%AE%9A%E5%88%B0%E5%8F%A6%E5%A4%96%E4%B8%80%E4%B8%AA%E5%AF%B9%E8%B1%A1%E4%B8%8A%E8%BF%90%E8%A1%8C%E3%80%82%0A%09*%2F%0A%09return%20function()%7B%0A%09%09obj%5BstrFunc%5D.apply(obj%2C%20args)%3B%2F%2F%E5%B0%86%E5%8F%82%E6%95%B0%E4%BC%A0%E9%80%92%E7%BB%99%E6%8C%87%E5%AE%9A%E7%9A%84%E4%BA%8B%E4%BB%B6%E5%A4%84%E7%90%86%E7%A8%8B%E5%BA%8F%0A%09%7D%0A%7D" /><param name="src" value="http://plkong.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf" /><param name="quality" value="high" /></object></div>
</div>
<ol class="dp-c">
<li><span><span class="comment">/* 将参数的函数封装为无参数的函数 */</span><span> </span></span></li>
<li><span><span class="keyword">function</span><span> createFunction(obj, strFunc){ </span></span></li>
<li><span> <span class="keyword">var</span><span> args = [];</span><span class="comment">//定义args用于存储传递给事件处理程序的参数</span><span> </span></span></li>
<li><span> <span class="keyword">if</span><span>(!obj) obj = window;</span><span class="comment">//如果是全局函数则obj = window;</span><span> </span></span></li>
<li><span> <span class="comment">//得到传递给事件处理程序的参数</span><span> </span></span></li>
<li><span> <span class="keyword">for</span><span>( </span><span class="keyword">var</span><span> i = 2; i&lt;arguments.length; i++) </span></span></li>
<li><span> args.push(arguments[i]); </span></li>
<li><span> <span class="comment">//用无参函数封装事件处理程序的调用</span><span> </span></span></li>
<li><span> <span class="comment">/*</span> </span></li>
<li><span><span class="comment"> <a href="http://www.iwanna.cn/tags/javascript/" class="st_tag internal_tag" rel="tag nofollow" title="Posts tagged with JavaScript">JavaScript</a>为函数对象定义了两个方法：apply 和 call， 它们的作用都是函数绑定到另外一个对象上运行。</span> </span></li>
<li><span><span class="comment"> */</span><span> </span></span></li>
<li><span> <span class="keyword">return</span><span> </span><span class="keyword">function</span><span>(){ </span></span></li>
<li><span> obj[strFunc].apply(obj, args);<span class="comment">//将参数传递给指定的事件处理程序</span><span> </span></span></li>
<li><span> } </span></li>
<li><span>} </span></li>
</ol>
</div>
<pre class="js" style="display: none;">/* 将参数的函数封装为无参数的函数 */
function createFunction(obj, strFunc){
	var args = [];//定义args用于存储传递给事件处理程序的参数
	if(!obj) obj = window;//如果是全局函数则obj = window;
	//得到传递给事件处理程序的参数
	for( var i = 2; i&lt;arguments.length; i++)
		args.push(arguments[i]);
	//用无参函数封装事件处理程序的调用
	/*
	<a href="http://www.iwanna.cn/tags/javascript/" class="st_tag internal_tag" rel="tag nofollow" title="Posts tagged with JavaScript">JavaScript</a>为函数对象定义了两个方法：apply 和 call， 它们的作用都是函数绑定到另外一个对象上运行。
	*/
	return function(){
		obj[strFunc].apply(obj, args);//将参数传递给指定的事件处理程序
	}
}</pre>
<p><span> 该方法将一个有参数的函数封装为一个无参数的函数，不仅对全局函数适用，作为 对象方法存在的函数也是适用的。该方法首先接收两个参数：obj 和 strFunc ，obj 表示事件处理程序所在的对象； strFunc 表示事件处理程序的名称。程序中还利用了arguments对象（arguments是传递给函数的隐含参数，arguments对象存储的是实际传递给 函数的参数，而且不局限与函数声明所定义的形参列表。关于arguments对象可以参考其他资料）处理第二个参数以后的隐式参数，即未定义为形参的参 数，</span></p>
<p><span> 例如：事件处理程序</span></p>
<p><span> someObject.eventHandler = function(_arg1, _arg2){</span></p>
<p><span> //事件处理代码<br />
</span></p>
<p><span>}</span></p>
<p><span> 应该调用：creatFunction(someObject, &#8220;eventHander&#8221;, arg1, arg2);</span></p>
<p><span> 这样就返回一个无参数的函数，在返回的函数中已经包括了传递进去的参数。如果是全局函数作为事件处理程序，事实上它是window 对象的一个方法，所以可以传递window对象作为obj参数，为了更清晰一点，也可以指定obj为null， creatFunction函数内部会自动认为该函数是全局函数，从而自动吧obj赋值为window。最后完成的代码如下：</span></p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Js代码 <object width="14" height="15" data="http://plkong.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf" type="application/x-shockwave-flash"><param name="flashvars" value="clipboard=%3Cscript%20language%3D%22javascript%22%20type%3D%22text%2Fjavascript%22%20%3E%0A%2F*%20%E5%B0%86%E5%8F%82%E6%95%B0%E7%9A%84%E5%87%BD%E6%95%B0%E5%B0%81%E8%A3%85%E4%B8%BA%E6%97%A0%E5%8F%82%E6%95%B0%E7%9A%84%E5%87%BD%E6%95%B0%20*%2F%0Afunction%20createFunction(obj%2C%20strFunc)%7B%0A%09var%20args%20%3D%20%5B%5D%3B%2F%2F%E5%AE%9A%E4%B9%89args%E7%94%A8%E4%BA%8E%E5%AD%98%E5%82%A8%E4%BC%A0%E9%80%92%E7%BB%99%E4%BA%8B%E4%BB%B6%E5%A4%84%E7%90%86%E7%A8%8B%E5%BA%8F%E7%9A%84%E5%8F%82%E6%95%B0%0A%09if(!obj)%20obj%20%3D%20window%3B%2F%2F%E5%A6%82%E6%9E%9C%E6%98%AF%E5%85%A8%E5%B1%80%E5%87%BD%E6%95%B0%E5%88%99obj%20%3D%20window%3B%0A%09%2F%2F%E5%BE%97%E5%88%B0%E4%BC%A0%E9%80%92%E7%BB%99%E4%BA%8B%E4%BB%B6%E5%A4%84%E7%90%86%E7%A8%8B%E5%BA%8F%E7%9A%84%E5%8F%82%E6%95%B0%0A%09for(%20var%20i%20%3D%202%3B%20i%3Carguments.length%3B%20i%2B%2B)%0A%09%09args.push(arguments%5Bi%5D)%3B%0A%09%2F%2F%E7%94%A8%E6%97%A0%E5%8F%82%E5%87%BD%E6%95%B0%E5%B0%81%E8%A3%85%E4%BA%8B%E4%BB%B6%E5%A4%84%E7%90%86%E7%A8%8B%E5%BA%8F%E7%9A%84%E8%B0%83%E7%94%A8%0A%09%2F*%0A%09JavaScript%E4%B8%BA%E5%87%BD%E6%95%B0%E5%AF%B9%E8%B1%A1%E5%AE%9A%E4%B9%89%E4%BA%86%E4%B8%A4%E4%B8%AA%E6%96%B9%E6%B3%95%EF%BC%9Aapply%20%E5%92%8C%20call%EF%BC%8C%20%E5%AE%83%E4%BB%AC%E7%9A%84%E4%BD%9C%E7%94%A8%E9%83%BD%E6%98%AF%E5%87%BD%E6%95%B0%E7%BB%91%E5%AE%9A%E5%88%B0%E5%8F%A6%E5%A4%96%E4%B8%80%E4%B8%AA%E5%AF%B9%E8%B1%A1%E4%B8%8A%E8%BF%90%E8%A1%8C%E3%80%82%0A%09*%2F%0A%09return%20function()%7B%0A%09%09obj%5BstrFunc%5D.apply(obj%2C%20args)%3B%2F%2F%E5%B0%86%E5%8F%82%E6%95%B0%E4%BC%A0%E9%80%92%E7%BB%99%E6%8C%87%E5%AE%9A%E7%9A%84%E4%BA%8B%E4%BB%B6%E5%A4%84%E7%90%86%E7%A8%8B%E5%BA%8F%0A%09%7D%0A%7D%0A%2F**%2F%0Afunction%20User()%7B%0A%7D%0AUser.prototype%3D%7B%0A%09show%3Afunction()%7B%0A%09%09this.onShow()%3B%2F%2F%E8%A7%A6%E5%8F%91onShow%E4%BA%8B%E4%BB%B6%0A%09%7D%2C%0A%09onShow%3Afunction()%7B%7D%2F%2F%E5%AE%9A%E4%B9%89%E4%BA%8B%E4%BB%B6%E6%8E%A5%E5%8F%A3%0A%7D%0Avar%20obj%20%3D%20new%20User()%3B%0A%2F%2F%E5%88%9B%E5%BB%BAobj%E7%9A%84onshow%E4%BA%8B%E4%BB%B6%E5%A4%84%E7%90%86%E7%A8%8B%E5%BA%8F%0Afunction%20objOnShow(userName)%7B%0A%09alert(%22hello%2C%22%2BuserName)%3B%0A%7D%0A%2F%2F%E5%AE%9A%E4%B9%89username%E5%8F%98%E9%87%8F%0Avar%20userName%20%3D%20%22plkong%22%3B%0A%2F%2F%E7%BB%91%E5%AE%9Aobj%E7%9A%84onshow%E4%BA%8B%E4%BB%B6%0A%2F%2Fobj.onShow%20%3D%20objOnShow%3B%2F%2F%E6%97%A0%E6%B3%95%E5%B0%86userName%E8%BF%99%E4%B8%AA%E5%8F%98%E9%87%8F%E4%BC%A0%E9%80%92%E8%BF%9B%E5%8E%BB%0Aobj.onShow%20%3D%20createFunction(null%2C%20%22objOnShow%22%2C%20userName)%3B%0Aobj.show()%3B%0A%3C%2Fscript%3E" /><param name="src" value="http://plkong.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf" /><param name="quality" value="high" /></object></div>
</div>
<ol class="dp-c">
<li><span><span>&lt;script language=</span><span class="string">&#8220;<a href="http://www.iwanna.cn/tags/javascript/" class="st_tag internal_tag" rel="tag nofollow" title="Posts tagged with JavaScript">javascript</a>&#8221;</span><span> type=</span><span class="string">&#8220;text/<a href="http://www.iwanna.cn/tags/javascript/" class="st_tag internal_tag" rel="tag nofollow" title="Posts tagged with JavaScript">javascript</a>&#8221;</span><span> &gt; </span></span></li>
<li><span><span class="comment">/* 将参数的函数封装为无参数的函数 */</span><span> </span></span></li>
<li><span><span class="keyword">function</span><span> createFunction(obj, strFunc){ </span></span></li>
<li><span> <span class="keyword">var</span><span> args = [];</span><span class="comment">//定义args用于存储传递给事件处理程序的参数</span><span> </span></span></li>
<li><span> <span class="keyword">if</span><span>(!obj) obj = window;</span><span class="comment">//如果是全局函数则obj = window;</span><span> </span></span></li>
<li><span> <span class="comment">//得到传递给事件处理程序的参数</span><span> </span></span></li>
<li><span> <span class="keyword">for</span><span>( </span><span class="keyword">var</span><span> i = 2; i&lt;arguments.length; i++) </span></span></li>
<li><span> args.push(arguments[i]); </span></li>
<li><span> <span class="comment">//用无参函数封装事件处理程序的调用</span><span> </span></span></li>
<li><span> <span class="comment">/*</span> </span></li>
<li><span><span class="comment"> <a href="http://www.iwanna.cn/tags/javascript/" class="st_tag internal_tag" rel="tag nofollow" title="Posts tagged with JavaScript">JavaScript</a>为函数对象定义了两个方法：apply 和 call， 它们的作用都是函数绑定到另外一个对象上运行。</span> </span></li>
<li><span><span class="comment"> */</span><span> </span></span></li>
<li><span> <span class="keyword">return</span><span> </span><span class="keyword">function</span><span>(){ </span></span></li>
<li><span> obj[strFunc].apply(obj, args);<span class="comment">//将参数传递给指定的事件处理程序</span><span> </span></span></li>
<li><span> } </span></li>
<li><span>} </span></li>
<li><span><span class="comment">/**/</span><span> </span></span></li>
<li><span><span class="keyword">function</span><span> User(){ </span></span></li>
<li><span>} </span></li>
<li><span>User.prototype={ </span></li>
<li><span> show:<span class="keyword">function</span><span>(){ </span></span></li>
<li><span> <span class="keyword">this</span><span>.onShow();</span><span class="comment">//触发onShow事件</span><span> </span></span></li>
<li><span> }, </span></li>
<li><span> onShow:<span class="keyword">function</span><span>(){}</span><span class="comment">//定义事件接口</span><span> </span></span></li>
<li><span>} </span></li>
<li><span><span class="keyword">var</span><span> obj = </span><span class="keyword">new</span><span> User(); </span></span></li>
<li><span><span class="comment">//创建obj的onshow事件处理程序</span><span> </span></span></li>
<li><span><span class="keyword">function</span><span> objOnShow(userName){ </span></span></li>
<li><span> alert(<span class="string">&#8220;hello,&#8221;</span><span>+userName); </span></span></li>
<li><span>} </span></li>
<li><span><span class="comment">//定义username变量</span><span> </span></span></li>
<li><span><span class="keyword">var</span><span> userName = </span><span class="string">&#8220;plkong&#8221;</span><span>; </span></span></li>
<li><span><span class="comment">//绑定obj的onshow事件</span><span> </span></span></li>
<li><span><span class="comment">//obj.onShow = objOnShow;//无法将userName这个变量传递进去</span><span> </span></span></li>
<li><span>obj.onShow = createFunction(<span class="keyword">null</span><span>, </span><span class="string">&#8220;objOnShow&#8221;</span><span>, userName); </span></span></li>
<li><span>obj.show(); </span></li>
<li><span>&lt;/script&gt;<br />
</span></li>
</ol>
</div>
<hr />
<p>© <a href="http://www.iwanna.cn">我想网</a> Akon 所有 , 2009. |
<a href="http://www.iwanna.cn/archives/2009/04/01/134/">永久链接</a> |
<a href="http://www.iwanna.cn/archives/2009/04/01/134/#comments">没有评论</a> |
提交到
<a rel="nofollow" target="_blank" href="http://www.google.com/reader/view/feed/http://www.iwanna.cn/archives/2009/04/01/134/">Google Reader</a>
<a rel="nofollow" target="_blank" href="http://www.xianguo.com/subscribe.php?url=http://www.iwanna.cn/archives/2009/04/01/134/">鲜果</a>
<a rel="nofollow" target="_blank" href="http://www.zhuaxia.com/add_channel.php?url=http://www.iwanna.cn/archives/2009/04/01/134/">抓虾</a>
<hr />
</p>
	标签：<a href="http://www.iwanna.cn/topics/ui/javascript/" title="JavaScript" rel="tag nofollow">JavaScript</a>, <a href="http://www.iwanna.cn/tags/javascript/" title="JavaScript" rel="tag nofollow">JavaScript</a>, <a href="http://www.iwanna.cn/topics/develope/patten/" title="Patten" rel="tag nofollow">Patten</a>, <a href="http://www.iwanna.cn/tags/patten/" title="Patten" rel="tag nofollow">Patten</a><br />

	<h2 class="related_post">您可能会感兴趣的其他文章</h2>
	<ul class="st-related-posts">
	<li><a href="http://www.iwanna.cn/archives/2009/04/29/894/" title="页面输出时一些常用的小技巧 (2009年04月29日)">页面输出时一些常用的小技巧</a> </li>
	<li><a href="http://www.iwanna.cn/archives/2010/06/12/3907/" title="适配器模式 (2010年06月12日)">适配器模式</a> </li>
	<li><a href="http://www.iwanna.cn/archives/2009/04/02/149/" title="设计模式之单例模式 (2009年04月2日)">设计模式之单例模式</a> </li>
	<li><a href="http://www.iwanna.cn/archives/2009/03/31/79/" title="表单元素：40个CSS/JS风格和功能技术处理 (2009年03月31日)">表单元素：40个CSS/JS风格和功能技术处理</a> </li>
	<li><a href="http://www.iwanna.cn/archives/2010/07/06/4383/" title="网页打开新窗口的解决方案,拒绝屏蔽 (2010年07月6日)">网页打开新窗口的解决方案,拒绝屏蔽</a> </li>
	<li><a href="http://www.iwanna.cn/archives/2010/07/05/4365/" title="类的设计原则 (2010年07月5日)">类的设计原则</a> </li>
	<li><a href="http://www.iwanna.cn/archives/2010/04/29/2874/" title="相见恨晚的一些 JavaScript 技巧 (2010年04月29日)">相见恨晚的一些 JavaScript 技巧</a> </li>
	<li><a href="http://www.iwanna.cn/archives/2009/09/18/2252/" title="用JS制作的网页版NES模拟器 IE8直接出局 (2009年09月18日)">用JS制作的网页版NES模拟器 IE8直接出局</a> </li>
	<li><a href="http://www.iwanna.cn/archives/2009/03/31/77/" title="用css+js控制图片大小的方法 (2009年03月31日)">用css+js控制图片大小的方法</a> </li>
	<li><a href="http://www.iwanna.cn/archives/2010/02/17/2516/" title="有关 JavaScript 的 10 件让人费解的事情 (2010年02月17日)">有关 JavaScript 的 10 件让人费解的事情</a> </li>
</ul>


<p><small>Feed enhanced by <a href='http://planetozh.com/blog/my-projects/wordpress-plugin-better-feed-rss/'>Better Feed</a> from  <a href='http://planetozh.com/blog/'>Ozh</a></small></p>
]]></content:encoded>
			<wfw:commentRss>http://www.iwanna.cn/archives/2009/04/01/134/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
