??xml version="1.0" encoding="utf-8" standalone="yes"?>
]]>
public class 敌h {
private ?my;
public 敌h() {
my = new ?); /* 哈哈,敌h内部的我Ҏ报h?*/
}
public void q?) {
System.out.println("--敌进--");
my.退();
}
public void ?) {
System.out.println("--敌驻--");
my.?);
}
public void ?) {
System.out.println("--敌疲--");
my.?);
}
public void 退() {
System.out.println("--敌退--");
my.q?);
}
}
public class ?{
public void 退() {
System.out.println("--我退--");
}
public void ?) {
System.out.println("--我扰--");
}
public void ?) {
System.out.println("--我打--");
}
public void q?) {
System.out.println("--我追--");
}
}
深入出单实例Singleton设计模式
陈皓
单实例Singleton设计模式可能是被讨论和用的最q泛的一个设计模式了Q这可能也是面试中问得最多的一个设计模式了。这个设计模式主要目? 是想在整个系l中只能出现一个类的实例。这样做当然是有必然的,比如你的软g的全局配置信息Q或者是一个FactoryQ或是一个主控类Q等{。你希望q? 个类在整个系l中只能出现一个实例。当Ӟ作ؓ一个技术负责h的你Q你当然有权利通过使用非技术的手段来达C的目的。比如:你在团队内部明文? 定,“XXcd能有一个全局实例Q如果某Z用两ơ以上,那么该h被处于2000元的|款Q?#8221;Q呵呵)Q你当然有权q么做。但是如果你的设计的是东西是 一个类库,或是一个需要提供给用户使用的APIQ恐怕你的这规定将会失效。因为,你无权要求别Z那么做。所以,q就是ؓ什么,我们希望通过使用技术的 手段来达成这样一个目的的原因?/p>
本文会带着你深入整个Singleton的世界,当然Q我会放弃用C++语言而改用Java语言Q因Z用Javaq个语言可能更容易让我说明一些事情?/p>
q里Q我直接给Z个Singleton的简单实玎ͼ因ؓ我相信你已经有这斚w的一些基了。我们姑且把q具版本叫做1.0?/p>
在上面的实例中,我想说明下面几个Singleton的特点:Q下面这些东西可能是h皆知的,没有什么新鲜的Q?/p>
当然Q如果你觉得知道了上面这些事情后学成了Q那我给你当头棒喝一下了Q事情远q没有那么简单?/p>
上面的这个程序存在比较严重的问题Q因为是全局性的实例Q所以,在多U程情况下,所有的全局׃n的东襉K会变得非常的危险Q这个也一P在多U程? 况下Q如果多个线E同时调用getInstance()的话Q那么,可能会有多个q程同时通过 (singleton== null)的条件检查,于是Q多个实例就创徏出来Qƈ且很可能造成内存泄露问题。嗯Q熟悉多U程的你一定会说—?#8220;我们需要线E互斥或同步”Q没错,我们 需要这个事情,于是我们的Singleton升?.1版,如下所C:
嗯,使用了Java的synchronizedҎQ看h不错哦。应该没有问题了吧?Q错Q这q是有问题!Z么呢Q前面已l说q,如果有多个线 E同旉过(singleton== null)的条件检查(因ؓ他们q行q行Q,虽然我们的synchronizedҎ会帮助我们同步所有的U程Q让我们q行U程变成串行的一个一个去 newQ那不还是一L吗?同样会出现很多实例。嗯Q确实如此!看来Q还得把那个判断(singleton== null)条g也同步v来。于是,我们的Singleton再次升?.2版本Q如下所C:
不错不错Q看似很不错了。在多线E下应该没有什么问题了Q不是吗Q的是q样的,1.2版的Singleton在多U程下的没有问题了Q因为我? 同步了所有的U程。只不过?#8230;…Q什么?Q还不行Q!是的Q还是有点小问题Q我们本来只是想让newq个操作q行可以了Q现在,只要是进? getInstance()的线E都得同步啊Q注意,创徏对象的动作只有一ơ,后面的动作全是读取那个成员变量,q些d的动作不需要线E同步啊。这L 作法感觉非常极端啊,Z一个初始化的创建动作,居然让我们达上了所有的L作,严重影响后箋的性能啊!
q得改!嗯,看来Q在U程同步前还得加一?singleton== null)的条件判断,如果对象已经创徏了,那么׃需要线E的同步了。OKQ下面是1.3版的Singleton?/p>
感觉代码开始变得有点罗嗦和复杂了,不过Q这可能是最不错的一个版本了Q这个版本又?#8220;双重?#8221;Double-Check。下面是说明Q?/p>
相当不错啊,q得非常漂亮Q请大家为我们的1.3版v立鼓掌!
怎么Q还有问题?Q当然还有,误住下面这条规则—?#8220;无论你的代码写得有多好,其只能在特定的范围内工作Q超个范围就要出Bug?/strong>”Q这?#8220;陈式W一定理”Q呵c你能想一惌有什么情况会让这个我们上面的代码出问题吗Q?/p>
在C++下,我不是很好D例,但是在Java的环境下Q嘿嘿,q是让我们来看看下面的一些反例和一些别的事情的讨论Q?strong>当然Q有些反例可能属于钻牛角,可能有点学院z,不过也不排除其实际可能性,q是提个醒?/strong>Q: 其一、Class Loader。不知道你对Java的Class
Loader熟悉吗?“c装载器”Q!C++可没有这个东西啊。这是Java动态性的核心。顾名思义Q类装蝲器是用来把类(class)装蝲qJVM的?
JVM规范定义了两U类型的c装载器Q启动内装蝲?bootstrap)和用戯定义装蝲?user-defined class
loader)?
在一个JVM中可能存在多个ClassLoaderQ每个ClassLoader拥有自己的NameSpace。一个ClassLoader只能拥有一?
class对象cd的实例,但是不同的ClassLoader可能拥有相同的class对象实例Q这时可能生致命的问题。如ClassLoaderAQ?
装蝲了类A的类型实例A1Q而ClassLoaderBQ也装蝲了类A的对象实例A2。逻辑上讲A1=A2Q但是由于A1和A2来自于不同的
ClassLoaderQ它们实际上是完全不同的Q如果A中定义了一个静态变量cQ则c在不同的ClassLoader中的值是不同的?/p>
于是Q如果咱们的Singleton 1.3版本如果面对着多个Class Loader会怎么P呵呵Q多个实例同样会被多个Class
Loader创徏出来Q当Ӟq个有点牵强Q不q他实存在。难道我们还要整Z1.4版吗Q可是,我们怎么可能在我的SingletoncM操作
Class Loader啊?是的Q你Ҏ不可能。在q种情况下,你能做的只有是—?#8220;保证多个Class
Loader不会装蝲同一个Singleton”?/p>
其二、序例化?/strong>如果我们的这个SingletoncL一个关于我们程序配|信息的cR我们需要它有序列化的功能,那么Q当反序列化的时候,我们无法控制别Z多次反序列化。不q,我们可以利用一下Serializable接口的readResolve()ҎQ比如: 其三、多个Java虚拟机?/strong>如果我们的程序运行在多个Java的虚拟机中。什么?多个虚拟机?q是一U什么样的情况啊。嗯Q这U情冉|有点极端Q不q还是可能出玎ͼ比如EJB或RMI之流的东ѝ要在这U环境下避免多实例,看来只能通过良好的设计或非技术来解决了?/p>
其四Qvolatile变量?/strong>关于volatileq个关键字所声明的变量可以被看作是一U?
“E度较轻的同步synchronized”Q与 synchronized 块相比,volatile
变量所需的编码较,q且q行时开销也较,但是它所能实现的功能也仅是synchronized的一部分。当Ӟ如前面所qͼ我们需要的
Singleton只是在创建的时候线E同步,而后面的d则不需要同步。所以,volatile变量q不能帮助我们即能解决问题,又有好的性能。而且Q?
q种变量只能在JDK 1.5+版后才能使用?/p>
其五、关于ѝ?/strong>是的Q承于Singleton后的子类也有可能造成多实例的问题。不q,因ؓ我们早把Singleton的构造函数声明成了私有的Q所以也杜l了l承q种事情?/p>
其六Q关于代码重用?/strong>也话我们的系l中有很多个c需要用到这个模式,如果我们在每一个类都中有这L代码Q那?
显得有点傻了。那么,我们是否可以使用一U方法,把这h式抽象出去?在C++下这是很Ҏ的,因ؓ有模板和友元Q还支持栈上分配内存Q所以比较容易一
些(E序如下所C)QJava下可能比较复杂一些,聪明的你知道怎么做吗Q?/p>
(转蝲时请注明作者和出处。未l许可,请勿用于商业用?/span>) Q全文完Q?/p>
预备知识Q?/p>
一、UML的特性与发展现状
UML是一ULanguage(语言)
UML是一UModeling(建模)Language
UML是Unified(l一)Modeling Language
1、已q入全面应用阶段的事实标?/p>
2、应用领域正在逐渐扩展Q包括嵌入式pȝ建模、业务徏模、流E徏模等多个领域
3、成?#8220;产生式编E?#8221;的重要支持技术:MDA?可执行UML{?/p>
二、徏模的目的与原?/p>
1、帮助我们按照实际情冉|按我们需要的样式对系l进行可视化;提供一U详l说明系l的l构或行为的Ҏ;l出一个指导系l构造的模板;Ҏ们所做出的决{进行文档化?/p>
2、仅当需要模型时Q才构徏它?/p>
3、选择要创Z么模型对如何动手解决问题和如何Ş成解x案有着意义p的媄?每一U模型可以在不同的精度别上表示;最好的模型是与现实相联pȝ;单个模型是不充分的。对每个重要的系l最好用一l几乎独立的模型d理?/p>
三、谁应该建模
1、业务徏模:以领域专家ؓ主,需求分析h员是dQ系l分析员、架构师可参?/p>
2、需求模型:以需求分析h员ؓ主,pȝ分析员是dQ领域专家提供指|架构师和资深开发h员参?/p>
3、设计模型:高层设计模型以架构师ZQ系l分析员从需求方面提供支持,资深开发h员从技术实现方面提供支持。详l设计模型则以资深开发h员ؓ主,架构师提供指对{?/p>
4、实现模型:以资深开发h?设计人员)ZQ架构师提供M指导?/p>
5?a class="fllink" target="_bank">数据?/a>模型Q以数据库开发h员ؓ主,架构师提供指|资深开发h?设计人员)予以配合?/p>
正式开?/p>
UMLl成Q三部分(构造块、规则、公共机?Q关pd下图所C:
一、构造块
1、构造块是对模型中最h代表性的成分的抽?/p>
建模元素QUML中的名词Q它是模型基本物理元素?/p>
行ؓ元素QUML中的动词Q它是模型中的动态部分,是一U跨时间、空间的行ؓ?/p>
分组元素QUML中的容器Q用来组l模型,使模型更加的l构化?/p>
注释元素QUML中的解释部分Q和代码中的注释语句一P是用来描q模型的?/p>
1.1、徏模元?/p>
c?class)和对?object)
接口(interface)
dc?active class)
用例(use case)
协作(collaboration)
构g(component)
节点(node)
c?class)和对?object)
cL对一l具有相同属性、相同操作、相同关pd相同语义的对象的抽象
UML中类是用一个矩形表C的Q它包含三个区域Q最上面是类名、中间是cȝ属性、最下面是类的方?/p>
对象则是cȝ一个实?(object is a Instance of Class)
接口(interface)
接口是描q某个类或构件的一个服务操作集
dc?active class)
dcd际上是一U特D的cR引用它的原因,实际上是在开发中需要有一些类能够起到 启动控制zd的作?/p>
dcL指其对象臛_拥有一个进 E或U程Q能够启动控制活动的c?/p>
用例(use case)
用例是著名的大师Ivar Jacobson首先提出的,现已l成Z面向对象软g开发中一个需求分析的最常用工具
用例实例是在pȝ中执行的一pd动作Q这些动作将生成特定执行者可见的价值结果。一?用例定义一l用例实例?/p>
协作(collaboration)
协作定义了一个交互,它是׃l共同工作以提供某协作行为的角色和其他元素构 成的一个群体?/p>
对于某个用例的实现就?以表CZؓ一个协?/p>
构g(component)
在实际的软gpȝ中,有许多要?#8220;c?#8221;更大的实体,例如一个COMlg、一个DLL文g、一个JavaBeans、一个执行文件等{。ؓ了更好地对在UML模型中对它们q行表示Q就引入了构?也译为组?
构g是系l设计的一个模块化部分Q它隐藏了内部的实现Q对外提供了一l外部接口。在pȝ中满相同接口的lg可以自由地替?/p>
节点(node)
Z能够有效地对部v的结构进行徏模,UML引入了节点这一概念Q它可以用来描述实际的PC机、打印机?a class="fllink" target="_bank">服务?/a>{Y件运行的基础g
节点是运行时存在的物理元素,它表CZ一U可计算的资源,通常臛_有存储空间和处理能力
1.2、行为元?/p>
交互(interaction)Q?是在特定语境中,共同完成某个d的一l对象之间交换的信息集合
交互的表C法很简单,是一条有向直U,q在上面标有操作?/p>
状态机(state machine)Q是一个对象或交互在生命周期内响应事g所l历的状态序?/p>
在UML模型中将状态画Z个圆 角矩形,q在矩Ş内写出状态名 U及其子状?/p>
1.3、分l元?/p>
对于一个中大型的Y件系l而言Q通常会包含大量的c,因此也就会存在大量的l构事物、行Z物,Z能够更加有效地对其进行整合,生成或简或繁、或宏观或微观的模型Q就需要对其进行分l。在UML中,提供?#8220;?Package)”来完成这一目标
1.4、注释元?/p>
l构事物是模型的主要构造块Q行Z物则是补充了模型中的动态部分,分组事物而是用来更好地组l模型,g已经很完整了。而注释事物则是用来锦上添qQ它是用来在UML模型上添加适当的解释部?/p>
2、关p?/p>
UML模型的关pL较多,下图
2.1 兌关系
兌(Association)表示两个cM间存在某U语义上的联pR关联关pL供了通信的\径,它是所有关pM最通用、语义最q?/p>
在UML中,使用一条实U来表示兌关系
在关联关pMQ有两种比较Ҏ的关p:聚合和组?/p>
聚合关系Q聚?Aggregation)是一U特DŞ式的兌。聚合表C类之间的关pL整体与部分的关系
如果发现“部分”cȝ存在Q是完全依赖?#8220;整体”cȝQ那么就应该使用“l合”关系来描q?/p>
l合是聚合的变种Q加入了一些重要的语义。也是_在一个组合关pM一个对象一ơ就只是一个组合的一部分Q?#8220;整体”负责“部分”的创建和破坏Q当“整体”被破坏时Q?#8220;部分”也随之消?/p>
聚合像汽R和R胎,汽R坏了胎还可以用。组合就像公司和下属部门Q公司倒闭了部门也׃存在?
2.2 泛化、实C依赖
泛化关系描述了一般事物与该事物中的特D种cM间的关系Q也是父类与子cM间的关系?/p>
实现关系是用来规定接口和实现接口的类或组件之间的关系。接口是操作的集合,q些操作用于规定cLlg的服务?/p>
有两个元素X、YQ如果修改元素X的定义可能会引v对另一个元素Y的定义的修改Q则U元素Y依赖(Dependency)于元素X?/p>
二、规?/p>
命名Q也是Z物、关pd图v名字。和M语言一P名字都是一个标识符
范围Q与cȝ作用域相?
可见性:PublicQProtectedQPrivateQPackage
三、UML公共机制
1、规格描q?/p>
在图形表C法的每个部分后面都有一个规格描q?也称q?Q它用来Ҏ造块的语法和语义q行文字叙述。这U构思,也就使可视化视图和文字视囄分离 Q?/p>
2、UML修饰与通用划分
在ؓ了更好的表示q些l节QUML中还提供了一些修饰符P例如不同可视性的W号、用斜体字表C抽象类
UML通用划分Q?/p>
1)cM对象的划分:cL一U抽象,对象是一个具?的实?/p>
2)接口与实现的分离Q接口是一U声明、是一个契 U,也是服务的入?实现则是负责实施接口提供 的契U?/p>
3、UML扩展机制
q部分不Ҏ描述Q待?邀月注 2009.2.18)
构造型Q在实际的徏模过E中Q可能会需要定义一些特定于某个领域或某个系l的构造块
标记值则是用来ؓ事物d新特性的。标记值的表示Ҏ是用形如“{标记信息}”的字W串
U束是用来增加新的语义或改变已存在规则的一U机?自由文本和OCL两种表示?。约束的表示法和标记值法cMQ都是用花括号括v来的串来表示Q不q它是不能够攑֜元素中的Q而是攑֜相关的元素附q?/p>
4、UML视图和图
囑 功能 备注
cd 描述cR类的特性以及类之间的关pR UML 1原有
对象图 描述一个时间点上系l中各个对象的一个快? UML 1非正式图
复合l构图 描述cȝq行时刻的分解 UML 2.0新增
构g图 描述构g的结构与q接 UML 1原有
部v图 描述在各个节点上的部|Ӏ UML 1原有
包图 描述~译时的层次l构 UML中非正式?/p>
用例图 描述用户与系l如何交互 UML 1原有
zd图 描述q程行ؓ与ƈ行行为 UML 1原有
状态机图 描述事g如何改变对象生命周期 UML 1原有
序图 描述对象之间的交互,重点在强调顺序 UML 1原有
通信图 描述对象之间的交互,重点在于q接 UML 1中的协作?/p>
定时图 描述对象之间的交互,重点在于定时 UML 2.0 新增
交互概观图 是一U顺序图与活动图的合 UML 2.0新增
附:开发过E与囄对应关系
目录 Q?br />
UML视图和图
一 Q静态视?
静态视?的主要组成部分是 cd关系 Q它昄为类?因ؓ它不描述旉相关的行为,因而是静态的Q?关系包括 Q关联,l承 和各U依赖,依赖包括实现和用。类间的关系l成q接cȝ路径Q不同种cȝ关系q上的l构和\?或端点上的修饰来区分?br />
例子 Q?br />
房应用的类?br />
图例 Q?br />
?Q用例图
用例作ؓ交互视图中的协作来实现的?主要从活动者的角度考虑?br />
?Q?交互视图
1 Q顺序图
2 Q协作图
?Q状态机视图
状态机昄为状态图
?Q活动视?br />
横条表示控制的分叉河q接
?Q物理视?
有两U物理视?Q实现视??配置视图
1 Q实现视?
实现视图昄为构件图
接口昄为具有哦名称的圆Q即相关的服务集 Q连接构件和接口的实U表C构件提供接口所列D的服务。从构gx口的虚线表明构g需要接口所提供的服?br />
2 Q配|视?
配置视图表达了运行时?构g实力在结点实例中的分布,l点是运行资源,如计机Q设备或内存Q该视图允许分布式的l果和资源分配被评估Q下面图中展CZpȝ中结点的U类和结Ҏ拥有构g的种c,节电昄为方块?br />
下图 ?一个实例别的配置?
?Q模型管理视?
最?来几张图
cd中的关系 Q?br />
1 Q一般化QGeneralization Q关p?
2 Q关? Association )关系
2.1 : 聚合QAggregation )关系
2.2 : 合成(Composition)关系
3 : 依赖 ( Dependency )关系
|
![]() |
U别Q?初 Scott W. Ambler (scott.ambler@ronin-intl.com), 总裁, Ronin International 2001 q?1 ?04 ?/p> 本文介绍了一些提高系l用例模型质量的技巧和技术。本文改~自 Object Primer 2nd Edition 的第 6 章?/blockquote> |
|
![]() |
U别Q?初 傅纯一, Rational中国区技术销售经? IBM中国有限公司软g?br /> 2004 q?11 ?01 ?/p> 用例(Use Case)是一U描q系l需求的ҎQ用用例的Ҏ来描q系l需求的q程是用例建模。用例方法最早是由Iva Jackboson博士提出的,后来被综合到UML规范之中Q成ZU标准化的需求表qCpR用例的使用在RUP中被推崇备至Q整个RUP程都被UC?用例驱动"(Use-Case Driven)的,各种cd的开发活动包括项目管理、分析设计、测试、实现等都是以系l用例ؓ主要输入工gQ用例模型奠定了整个pȝ软g开发的基础?/blockquote> |
原型模式定义:
用原型实例指定创建对象的U类,q且通过拯q些原型创徏新的对象.
Prototype模式允许一个对象再创徏另外一个可定制的对象,Ҏ无需知道M如何创徏的细?工作原理?通过一个原型对象传l那个要发动创徏的对象,q个要发动创建的对象通过h原型对象拯它们自己来实施创建?/p>
如何使用?
因ؓJava中的提供clone()Ҏ来实现对象的克隆,所以Prototype模式实现一下子变得很简?
以勺子ؓ例:
public abstract class AbstractSpoon implements Cloneable { String spoonName; public void setSpoonName(String spoonName) {this.spoonName = spoonName;} public String getSpoonName() {return this.spoonName;} public Object clone() { Object object = null; try { object = super.clone(); } catch (CloneNotSupportedException exception) { System.err.println("AbstractSpoon is not Cloneable"); } return object; } } |
有个具体实现(ConcretePrototype):
public class SoupSpoon extends AbstractSpoon
|
调用Prototype模式很简?
AbstractSpoon spoon = new SoupSpoon();
AbstractSpoon spoon2 = spoon.clone();
当然也可以结合工厂模式来创徏AbstractSpoon实例?/p>
在Java中Prototype模式变成clone()Ҏ的用,׃Java的纯z的面向对象Ҏ,使得在Java中用设计模式变得很自然Q两者已l几乎是然一体了。这反映在很多模式上Q如Interator遍历模式?/p>
Template模板模式定义:
定义一个操作中法的骨?一些步骤的执行延迟到其子类?
使用Java的抽象类Ӟq怼使用到Template模式,因此Template模式使用很普?而且很容易理解和使用?/p>
public abstract class Benchmark { /** * 下面操作是我们希望在子类中完?br /> */ public abstract void benchmark(); /** for (int i = 0; i < count; i++) |
在上例中,我们希望重复执行benchmark()操作,但是对benchmark()的具体内Ҏ有说?而是延迟到其子类中描q?
public class MethodBenchmark extends Benchmark { /** * 真正定义benchmark内容 */ public void benchmark() { for (int i = 0; i < Integer.MAX_VALUE; i++){ System.out.printtln("i="+i); } } } |
x,Template模式已经完成,是不是很?
我们UrepeatҎ为模板方法, 它其中的benchmark()实现被gq到子类MethodBenchmark中实CQ?/p>
看看如何使用:
Benchmark operation = new MethodBenchmark();
long duration = operation.repeat(Integer.parseInt(args[0].trim()));
System.out.println("The operation took " + duration + " milliseconds");
也许你以前还疑惑抽象cL什么用,现在你应该彻底明白了? 至于q样做的好处,很显然啊,扩展性强,以后Benchmark内容变化,我只要再做一个承子cd可以,不必修改其他应用代码.