??xml version="1.0" encoding="utf-8" standalone="yes"?>
对象以树Şl构l织h,以达成“部分-整体?的层ơ结构,使得客户端对单个对象和组合对象的使用h一致?
Composite比较Ҏ理解Q想到Composite应该想到树形结构图。组合体内这些对象都有共同接?当组合体一个对象的Ҏ被调用执行时QComposite遍?Iterator)整个树Şl构,L同样包含q个Ҏ的对象ƈ实现调用执行。可以用牵一动百来Ş宏V?/p>
所以Composite模式使用到Iterator模式Q和Chain of Responsibility模式cM?/p>
Composite好处:
1.使客L调用单,客户端可以一致的使用l合l构或其中单个对象,用户׃必关p自己处理的是单个对象还是整个组合结构,q就化了客户端代码?br />2.更容易在l合体内加入对象部g. 客户端不必因为加入了新的对象部g而更改代码?/p>
如何使用Composite?
首先定义一个接口或抽象c,q是设计模式通用方式了,其他设计模式Ҏ口内部定义限制不多,Composite却有个规定,那就是要在接口内部定义一个用于访问和理Compositel合体的对象们(或称部gComponentQ?
下面的代码是以抽象类定义Q一般尽量用接口interface,
public abstract class Equipment { private String name; //实h public abstract double netPrice(); //折扣h public abstract double discountPrice(); //增加部gҎ public boolean add(Equipment equipment) { return false; } //删除部gҎ public boolean remove(Equipment equipment) { return false; } //注意q里Q这里就提供一U用于访问组合体cȝ部gҎ?br /> public Iterator iter() { return null; } public Equipment(final String name) { this.name=name; } } |
抽象cEquipment是Component定义Q代表着l合体类的对象们,Equipment中定义几个共同的Ҏ?/p>
public class Disk extends Equipment { public Disk(String name) { super(name); } //定义Disk实h? public double netPrice() { return 1.; } //定义了disk折扣h?.5 Ҏ?br /> public double discountPrice() { return .5; } } |
Disk是组合体内的一个对象,或称一个部Ӟq个部g是个单独元素( Primitive)?br />q有一U可能是Q一个部件也是一个组合体Q就是说q个部g下面q有'儿子'Q这是树形结构中通常的情况,应该比较Ҏ理解。现在我们先要定义这个组合体Q?/p>
abstract class CompositeEquipment extends Equipment //注意q里Q这里就提供用于讉K自己l合体内的部件方法?br /> //上面dIsk 之所以没有,是因为Disk是个单独(Primitive)的元? |
上面CompositeEquipmentl承了Equipment,同时己里面的对象们提供了外部讉K的方?重蝲了Iterator,Iterator是Java的Collection的一个接口,是Iterator模式的实?
我们再看看CompositeEquipment的两个具体类:盘盒Chassis和箱子CabinetQ箱子里面可以放很多东西Q如底板Q电源盒Q硬盘盒{;盘盒里面可以放一些小讑֤Q如盘 软驱{。无疑这两个都是属于l合体性质的?/p>
public class Chassis extends CompositeEquipment { public Chassis(String name) { super(name); } public double netPrice() { return 1.+super.netPrice(); } public double discountPrice() { return .5+super.discountPrice(); } } public class Cabinet extends CompositeEquipment |
x我们完成了整个Composite模式的架构?/p>
我们可以看看客户端调用Composote代码:
Cabinet cabinet=new Cabinet("Tower");
Chassis chassis=new Chassis("PC Chassis");
//PC Chassis装到Tower?(盘盒装到箱子里)
cabinet.add(chassis);
//一?0GB的硬盘装?PC Chassis (硬盘装到盘盒里)
chassis.add(new Disk("10 GB"));
//调用 netPrice()Ҏ;
System.out.println("netPrice="+cabinet.netPrice());
System.out.println("discountPrice="+cabinet.discountPrice());
上面调用的方法netPrice()或discountPrice()Q实际上Composite使用Iterator遍历了整个树形结?L同样包含q个Ҏ的对象ƈ实现调用执行.
Composite是个很y妙体现智慧的模式Q在实际应用中,如果到树Şl构Q我们就可以试是否可以使用q个模式?/p>
以论坛ؓ例,一个版(forum)中有很多帖子(message),q些帖子有原始脓Q有对原始脓的回应脓Q是个典型的树Şl构Q那么当然可以用Composite模式Q那么我们进入Jive中看看,是如何实现的.
Jive解剖
在Jive?ForumThread是ForumMessages的容器container(l合?.也就是说QForumThreadcM我们上例中的 CompositeEquipment.它和messages的关pd图:
[thread]
|- [message]
|- [message]
|- [message]
|- [message]
|- [message]
我们在ForumThread看到如下代码Q?br />
public interface ForumThread { .... public void addMessage(ForumMessage parentMessage, ForumMessage newMessage) throws UnauthorizedException; public void deleteMessage(ForumMessage message) } |
cMCompositeEquipment, 提供用于讉K自己l合体内的部件方? 增加 删除 遍历.
l合我的其他模式中对Jive的分析,我们已经基本大体理解了Jive论坛体系的框Ӟ如果你之前不理解设计模式Q而直接去看Jive源代码,你肯定无法看懂?/p>
Decorator定义:
动态给一个对象添加一些额外的职责,p在墙上刷Ҏ.使用Decorator模式相比用生成子cL式达到功能的扩充昑־更ؓ灉|.
Z么用Decorator?
我们通常可以使用l承来实现功能的拓展,如果q些需要拓展的功能的种cdJ多,那么势必生成很多子类,增加pȝ的复杂?同时,使用l承实现功能拓展,我们必须可预见这些拓展功?q些功能是编译时q定了,是静态的.
使用Decorator的理由是:q些功能需要由用户动态决定加入的方式和时?Decorator提供?x即用"的方?在运行期间决定何时增加何U功?
如何使用?
举Adapter中的打桩CZ,在Adapter中有两种c?方Ş?圆Ş?Adapter模式展示如何l合使用q两个类,在Decorator模式?我们是要在打桩时增加一些额外功?比如,挖坑 在桩上钉木板{?不关心如何用两个不相关的类.
我们先徏立一个接?
public interface Work } |
接口Work有一个具体实?插入方Ş桩或圆Ş?q两个区别对Decorator是无所?我们以插入方形桩Z:
public class SquarePeg implements Work{ public void insert(){ System.out.println("方Ş桩插?); } } |
现在有一个应?需要在桩打入前,挖坑,在打入后,在桩上钉木板,q些额外的功能是动?可能随意增加调整修改,比如,可能又需要在打桩之后钉架?只是比喻).
那么我们使用Decorator模式,q里方Ş桩SquarePeg是decoratee(被刷Ҏ?,我们需要在decoratee上刷?Ҏ",q些Ҏ是那些额外的功?
public class Decorator implements Work{ private Work work; //在构造器中用组合new方式,引入Work对象; others.add("钉木?); public void insert(){ newMethod();}
public void otherMethod() } |
在上例中,我们把挖坑和钉木杉K排在了打桩insert前面,q里只是举例说明额外功能ơ序可以L安排.
好了,Decorator模式出来?我们看如何调?
Work squarePeg = new SquarePeg();
Work decorator = new Decorator(squarePeg);
decorator.insert();
Decorator模式x完成.
如果你细?会发?上面调用cM我们d文g时的调用:
FileReader fr = new FileReader(filename);
BufferedReader br = new BufferedReader(fr);
实际上Java 的I/O API是使用Decorator实现?/a>,I/O变种很多,如果都采取承方?会产生很多子类,昄相当J琐.
Jive中的Decorator实现
在论坛系l中,有些特别的字是不能出现在论坛中如"打倒XXX",我们需要过滤这?反动"的字?不让他们出现或者高亮度昄.
在IBM Java专栏中专?a target="_blank">谈Jive的文?/a>?有谈及Jive中ForumMessageFilter.java使用了Decorator模式,其实,该程序ƈ没有真正使用Decorator,而是提示?针对特别论坛可以设计额外增加的过滤功?那么可以重lForumMessageFilter作ؓDecorator模式?
所?我们在分辨是否真正是Decorator模式,以及会真正用Decorator模式,一定要把握好Decorator模式的定?以及其中参与的角?Decoratee 和Decorator).
Facade一个典型应用就是数据库JDBC的应?如下例对数据库的操作:
public class DBCompare { Connection conn = null; |
上例是Jsp中最通常的对数据库操作办?
在应用中,l常需要对数据库操?每次都写上述一D代码肯定比较麻?需要将其中不变的部分提炼出?做成一个接?q就引入了facade外观对象.如果以后我们更换Class.forName中的<driver>也非常方?比如从Mysql数据库换到Oracle数据?只要更换facade接口中的driver可?
我们做成了一?a target="_blank">Facade接口,使用该接?上例中的E序可以更改如?
public class DBCompare { |
可见非常?所有程序对数据库访问都是用改接口,降低pȝ的复杂?增加了灵zL?
如果我们要用连接池,也只要针对facade接口修改可?
׃囑֏以看? facade实际上是个理系l间关系,降低pȝ间耦合度的一个常用的办法,也许你已l不知不觉在使用,管不知道它是facade.
Z使用?
我们l常到要将两个没有关系的类l合在一起?W一解决Ҏ是:修改各自cȝ接口Q但是如果我们没有源代码Q或者,我们不愿意ؓ了一个应用而修改各自的接口?怎么?
使用AdapterQ在q两U接口之间创Z个合接?淯?.
如何使用?
实现Adapter方式,其实"think in Java"?cd?一节中已经提到,有两U方式:l合(composition)和?inheritance).
假设我们要打桩,有两U类Q方形桩 圆Ş?
public class SquarePeg{
public void insert(String str){
System.out.println("SquarePeg insert():"+str);
}
}
public class RoundPeg{
public void insertIntohole(String msg){
System.out.println("RoundPeg insertIntoHole():"+msg);
}
}
现在有一个应?需要既打方形桩,又打圆Ş?那么我们需要将q两个没有关pȝcȝ合应?假设RoundPeg我们没有源代?或源代码我们不想修改,那么我们使用Adapter来实现这个应?
public class PegAdapter extends SquarePeg{
private RoundPeg roundPeg;
public PegAdapter(RoundPeg peg)(this.roundPeg=peg;)
public void insert(String str){ roundPeg.insertIntoHole(str);}
}
在上面代码中,RoundPeg属于Adaptee,是被适配?PegAdapter是Adapter,Adaptee(被适配者RoundPeg)和Target(目标SquarePeg)q行适配.实际上这是将l合Ҏ(composition)和?inheritance)Ҏl合q用.
PegAdapter首先l承SquarePegQ然后用new的组合生成对象方式,生成RoundPeg的对象roundPegQ再重蝲父类insert()Ҏ。从q里,你也了解使用new生成对象和用extendsl承生成对象的不?前者无需对原来的cM?甚至无需要知道其内部l构和源代码.
如果你有些Java使用的经验,已经发现Q这U模式经怋用?/p>
q一步?/b>
上面的PegAdapter是承了SquarePeg,如果我们需要两边承,即承SquarePeg 又承RoundPeg,因ؓJava中不允许多承,但是我们可以实现(implements)两个接口(interface)
public interface IRoundPeg{
public void insertIntoHole(String msg);
}
public interface ISquarePeg{
public void insert(String str);
}
下面是新的RoundPeg 和SquarePeg, 除了实现接口q一区别Q和上面的没什么区别?br />public class SquarePeg implements ISquarePeg{
public void insert(String str){
System.out.println("SquarePeg insert():"+str);
}
}
public class RoundPeg implements IRoundPeg{
public void insertIntohole(String msg){
System.out.println("RoundPeg insertIntoHole():"+msg);
}
}
下面是新的PegAdapter,叫做two-way adapter:
public class PegAdapter implements IRoundPeg,ISquarePeg{
private RoundPeg roundPeg;
private SquarePeg squarePeg;
// 构造方?br /> public PegAdapter(RoundPeg peg){this.roundPeg=peg;}
// 构造方?br /> public PegAdapter(SquarePeg peg)(this.squarePeg=peg;)
public void insert(String str){ roundPeg.insertIntoHole(str);}
}
q有一U叫Pluggable Adapters,可以动态的获取几个adapters中一个。用Reflection技术,可以动态的发现cM的PublicҎ?/p>
代理模式是比较有用途的一U模?而且变种较多,应用场合覆盖从小l构到整个系l的大结?Proxy是代理的意?我们也许有代理服务器{概?代理概念可以解释?在出发点到目的地之间有一道中间层,意ؓ代理.
设计模式中定?/b>: 为其他对象提供一U代理以控制对这个对象的讉K.
Z么要使用Proxy?
1.授权机制 不同U别的用户对同一对象拥有不同的访问权?如Jive论坛pȝ?׃用Proxyq行授权机制控制,讉K论坛有两Uh:注册用户和游?未注册用?,Jive中就通过cMForumProxyq样的代理来控制q两U用户对论坛的访问权?
2.某个客户端不能直接操作到某个对象,但又必须和那个对象有所互动.
举例两个具体情况:
(1)如果那个对象是一个是很大的图?需要花费很长时间才能显C出?那么当这个图片包含在文档中时,使用~辑器或览器打开q个文档,打开文档必须很迅?不能{待大图片处理完?q时需要做个图片Proxy来代替真正的囄.
(2)如果那个对象在Internet的某个远端服务器?直接操作q个对象因ؓ|络速度原因可能比较?那我们可以先用Proxy来代曉K个对?
M原则?对于开销很大的对?只有在用它时才创徏,q个原则可以为我们节省很多宝늚Java内存. 所?有些为Java耗费资源内存,我以和程序编制思\也有一定的关系.
如何使用Proxy?
?a >Jive论坛pȝZ,讉K论坛pȝ的用h多种cd:注册普通用?论坛理?pȝ理?游客,注册普通用h能发a;论坛理者可以管理他被授权的论坛;pȝ理者可以管理所有事务等,q些权限划分和管理是使用Proxy完成?
Forum是Jive的核心接?在Forum中陈列了有关论坛操作的主要行?如论坛名U?论坛描述的获取和修改,帖子发表删除~辑{?
在ForumPermissions中定义了各种U别权限的用?
public class ForumPermissions implements Cacheable {
/** /** /** /** /** /** /** /** /** ..... public boolean isSystemOrForumAdmin() { ..... } |
因此,Forum中各U操作权限是和ForumPermissions定义的用L别有关系?作ؓ接口Forum的实?ForumProxy正是这U对应关p联pv?比如,修改Forum的名U?只有论坛理者或pȝ理者可以修?代码如下:
public class ForumProxy implements Forum { private ForumPermissions permissions; public void setName(String name) throws UnauthorizedException, ... } |
而DbForum才是接口Forum的真正实?以修改论坛名UCؓ?
public class DbForum implements Forum, Cacheable { public void setName(String name) throws ForumAlreadyExistsException { ....
} |
凡是涉及到对论坛名称修改q一事g,其他E序都首先得和ForumProxy打交?由ForumProxy军_是否有权限做某一样事?ForumProxy是个名副其实?|关","安全代理pȝ".
在^时应用中,无可避免总要涉及到系l的授权或安全体p?不管你有无意识的使用Proxy,实际你已l在使用Proxy?
我们l箋l合Jive谈入׃?下面要涉及到工厂模式?如果你不了解工厂模式,L我的另外一文?设计模式之Factory
我们已经知道,使用Forum需要通过ForumProxy,Jive中创Z个Forum是用Factory模式,有一个ȝ抽象cForumFactory,在这个抽象类?调用ForumFactory是通过getInstance()Ҏ实现,q里使用了Singleton(也是设计模式之一,׃介绍文章很多,我就不写?看这?/a>),getInstance()q回的是ForumFactoryProxy.
Z么不q回ForumFactory,而返回ForumFactory的实现ForumFactoryProxy?
原因是明昄,需要通过代理定是否有权限创建forum.
在ForumFactoryProxy中我们看C码如?
public class ForumFactoryProxy extends ForumFactory {
protected ForumFactory factory; public ForumFactoryProxy(Authorization authorization, ForumFactory factory, public Forum createForum(String name, String description) |
ҎcreateForumq回的也是ForumProxy, Proxyp一道墙,其他E序只能和Proxy交互操作.
注意到这里有两个Proxy:ForumProxy和ForumFactoryProxy. 代表两个不同的职?使用Forum和创建Forum;
至于Z么将使用对象和创建对象分开,q也是ؓ什么用Factory模式的原因所?是ؓ?装" "分派";换句话说,可能功能单一?方便l护修改.
Jive论坛pȝ中其他如帖子的创建和使用,都是按照Forumq个思\而来?
以上我们讨论了如何用Proxyq行授权机制的访?Proxyq可以对用户隐藏另外一U称为copy-on-write的优化方?拯一个庞大而复杂的对象是一个开销很大的操?如果拯q程?没有对原来的对象有所修改,那么q样的拷贝开销没有必?用代理gq这一拯q程.
比如:我们有一个很大的Collection,具体如hashtable,有很多客L会ƈ发同时访问它.其中一个特别的客户端要q行q箋的数据获?此时要求其他客户端不能再向hashtable中增加或删除 东东.
最直接的解x案是:使用collection的lock,让这特别的客L获得q个lock,q行q箋的数据获?然后再释放lock.
public void foFetches(Hashtable ht){
synchronized(ht){
//具体的连l数据获取动?.
}
}
但是q一办法可能锁住Collection会很长时?q段旉,其他客户端就不能讉K该Collection?
W二个解x案是cloneq个Collection,然后让连l的数据获取针对clone出来的那个Collection操作.q个Ҏ前提?q个Collection是可clone?而且必须有提供深度clone的方?Hashtable提供了对自qcloneҎ,但不是Key和value对象的clone,关于Clone含义可以参?a target="_blank">专门文章.
public void foFetches(Hashtable ht){
Hashttable newht=(Hashtable)ht.clone();
}
问题又来?׃是针对clone出来的对象操?如果原来的母体被其他客户端操作修改了, 那么对clone出来的对象操作就没有意义?
最后解x?我们可以{其他客L修改完成后再q行clone,也就是说,q个特别的客L先通过调用一个叫clone的方法来q行一pd数据获取操作.但实际上没有真正的进行对象拷?直至有其他客L修改了这个对象Collection.
使用Proxy实现q个Ҏ.q就是copy-on-write操作.
Proxy应用范围很广,现在行的分布计方式RMI和Corba{都是Proxy模式的应?
更多Proxy应用,?a target="_blank">http://www.research.umbc.edu/~tarr/cs491/lectures/Proxy.pdf
Sun公司?Explore the Dynamic Proxy APIDynamic Proxy Classes
Z使用?
工厂模式是我们最常用的模式了,著名的Jive论坛 ,大量用了工厂模式Q工厂模式在JavaE序pȝ可以说是随处可见?/span>
Z么工厂模式是如此常用Q因为工厂模式就相当于创建实例对象的newQ我们经常要ҎcClass生成实例对象Q如A a=new A() 工厂模式也是用来创徏实例对象的,所以以后new时就要多个心|是否可以考虑实用工厂模式Q虽然这样做Q可能多做一些工作,但会l你pȝ带来更大的可扩展性和量的修改量?/span>
我们以类SampleZQ?如果我们要创建Sample的实例对?
Sample sample=new Sample();
可是Q实际情冉|Q通常我们都要在创?span lang="EN-US">sample实例时做点初始化的工?比如赋?查询数据库等?/span>
首先Q我们想到的是,可以使用Sample的构造函敎ͼq样生成实例写?
Sample sample=new Sample(参数);
但是Q如果创?span lang="EN-US">sample实例时所做的初始化工作不是象赋DL单的事,可能是很长一D代码,如果也写入构造函CQ那你的代码很难看了Q就需要Refactor重整Q?/span>
Z么说代码很难看,初学者可能没有这U感觉,我们分析如下Q初始化工作如果是很长一D代码,说明要做的工作很多,很多工作装入一个方法中Q相当于很多鸡蛋放在一个篮子里Q是很危险的Q这也是有背?span lang="EN-US">Java面向对象的原则,面向对象的封?Encapsulation)和分z?Delegation)告诉我们Q尽量将长的代码分派“切剜y成每段Q将每段再“封装”v?减少D和D之间偶合联pL?Q这P׃风险分散,以后如果需要修改,只要更改每段Q不会再发生牵一动百的事情?/span>
在本例中Q首先,我们需要将创徏实例的工?/font>?font color="#ff1493">使用实例的工?/font>分开, 也就是说Q让创徏实例所需要的大量初始化工作从Sample的构造函C分离出去?/span>
q时我们需?span lang="EN-US">Factory工厂模式来生成对象了Q不能再用上面简单new Sample(参数)?/span>q有,如果Sample有个l承如MySample, 按照面向接口~程,我们需要将Sample抽象成一个接?现在Sample是接?有两个子cMySample 和HisSample .我们要实例化他们?如下:
Sample mysample=new MySample();
Sample hissample=new HisSample();
随着目的深?span lang="EN-US">,Sample可能q会"生出很多儿子出来", 那么我们要对q些儿子一个个实例?更糟p的?可能q要对以前的代码q行修改:加入后来生出儿子的实?q在传统E序中是无法避免?
但如果你一开始就有意识用了工厂模式,q些ȝ没有了.
工厂Ҏ
你会建立一个专门生?span lang="EN-US">Sample实例的工?
public class Factory{ public static Sample creator(int which){ //getClass 产生Sample 一般可使用动态类装蝲装入cR?br /> if (which==1) } } |
那么在你的程序中,如果要实例化Sample?׃?/span>
Sample sampleA=Factory.creator(1);
q样,在整个就不涉及到Sample的具体子c?辑ֈ装效果,也就减少错误修改的机?q个原理可以用很通俗的话来比?是具体事情做得多,容易范错误.q每个做q具体工作的人都深有体会,相反,官做得越?说出的话抽象越W统,范错误可能性就少.好象我们从编E序中也能悟Zh生道?呵呵.
使用工厂Ҏ 要注意几个角Ԍ首先你要定义产品接口Q如上面的Sample,产品接口下有Sample接口的实现类,如SampleA,其次要有一个factoryc,用来生成产品SampleQ如下图Q最双是生产的对象SampleQ?/p>
q一步稍微复杂一点,是在工厂类上进行拓展,工厂cM有承它的实现类concreteFactory?b>?/i>
抽象工厂
工厂模式中有: 工厂Ҏ(Factory Method) 抽象工厂(Abstract Factory).
q两个模式区别在于需要创建对象的复杂E度上。如果我们创建对象的Ҏ变得复杂?如上面工厂方法中是创Z个对象Sample,如果我们q有新的产品接口Sample2.
q里假设QSample有两个concretecSampleA和SamleBQ而Sample2也有两个concretecSample2A和SampleB2
那么Q我们就上例中Factory变成抽象c?共同部分封装在抽象cM,不同部分使用子类实现Q下面就是将上例中的Factory拓展成抽象工?
public abstract class Factory{ public abstract Sample creator(); public abstract Sample2 creator(String name); } public class SimpleFactory extends Factory{ public Sample creator(){ public Sample2 creator(String name){ } public class BombFactory extends Factory{ public Sample creator(){ public Sample2 creator(String name){ }
|
从上面看C个工厂各自生产出一套Sample和Sample2,也许你会疑问Qؓ什么我不可以用两个工厂方法来分别生Sample和Sample2?
抽象工厂q有另外一个关键要点,是因?SimpleFactory内,生Sample和生产Sample2的方法之间有一定联p,所以才要将q两个方法捆l在一个类中,q个工厂cL其本w特征,也许刉过E是l一的,比如Q制造工艺比较简单,所以名U叫SimpleFactory?/span>
在实际应用中Q工厂方法用得比较多一些,而且是和动态类装入器组合在一起应用,
举例
我们?span lang="EN-US">Jive的ForumFactoryZQ这个例子在前面的Singleton模式中我们讨Q现在再讨论其工厂模?
public abstract class ForumFactory { private static Object initLock = new Object(); public static ForumFactory getInstance(Authorization authorization) { try { //Now, q回 proxy.用来限制授权对forum的访?br /> return new ForumFactoryProxy(authorization, factory, //真正创徏forum的方法由l承forumfactory的子cd完成. .... }
|
因ؓ现在?span lang="EN-US">Jive是通过数据库系l存放论坛帖子等内容数据,如果希望更改为通过文gpȝ实现,q个工厂ҎForumFactory提供了提供动态接?
private static String className = "com.jivesoftware.forum.database.DbForumFactory";
你可以用自己开发的创徏forum的方法代替com.jivesoftware.forum.database.DbForumFactory可?
在上面的一D代码中一q了三U模?span lang="EN-US">,除了工厂模式?q有Singleton单态模?以及proxy模式,proxy模式主要用来授权用户对forum的访?因ؓ讉Kforum有两Uh:一个是注册用户 一个是游客guest,那么那么相应的权限就不一?而且q个权限是诏I整个系l的,因此建立一个proxy,cM|关的概?可以很好的达到这个效?
看看Java宠物店中的CatalogDAOFactory:
public class CatalogDAOFactory {
/** * 本方法制定一个特别的子类来实现DAO模式?br /> * 具体子类定义是在J2EE的部|描q器中?br /> */ public static CatalogDAO getDAO() throws CatalogDAOSysException { CatalogDAO catDao = null; try { InitialContext ic = new InitialContext(); String className =(String) ic.lookup(JNDINames.CATALOG_DAO_CLASS); catDao = (CatalogDAO) Class.forName(className).newInstance(); } catch (NamingException ne) { throw new CatalogDAOSysException(" } catch (Exception se) { throw new CatalogDAOSysException(" } return catDao; } } |
CatalogDAOFactory是典型的工厂ҎQcatDao是通过动态类装入器className获得CatalogDAOFactory具体实现子类Q这个实现子cdJava宠物店是用来操作catalog数据库,用户可以Ҏ数据库的cd不同Q定制自q具体实现子类Q将自己的子cdl与CATALOG_DAO_CLASS变量可以?/span>
由此可见Q工厂方法确实ؓpȝl构提供了非常灵zd大的动态扩展机Ӟ只要我们更换一下具体的工厂ҎQ系l其他地Ҏ需一点变换,有可能系l功能进行改头换面的变化?/p>
在很多操作中Q比如徏立目?数据库连接都需要这L单线E操作?/p>
q有, singleton能够被状态化; q样Q多个单态类在一起就可以作ؓ一个状态仓库一样向外提供服务,比如Q你要论坛中的帖子计数器Q每ơ浏览一ơ需要计敎ͼ单态类能否保持住这个计敎ͼq且能synchronize的安全自动加1Q如果你要把q个数字怹保存到数据库Q你可以在不修改单态接口的情况下方便的做到?/p>
另外斚wQSingleton也能够被无状态化。提供工h质的功能,
Singleton模式׃ؓ我们提供了这样实现的可能。用Singleton的好处还在于可以节省内存Q因为它限制了实例的个数Q有利于Java垃圾回收Qgarbage collectionQ?br />
我们常常看到工厂模式中类装入?class loader)中也用Singleton模式实现?因ؓ被装入的cd际也属于资源?br />
如何使用?
一般Singleton模式通常有几UŞ?
public class Singleton { private Singleton(){} //在自己内部定义自׃个实例,是不是很奇怪? private static Singleton instance = new Singleton(); //q里提供了一个供外部讉K本class的静态方法,可以直接讉K
|
W二UŞ?
public class Singleton {
private static Singleton instance = null; }
|
使用Singleton.getInstance()可以讉K单态类?/p>
上面W二中Ş式是lazy initializationQ也是说第一ơ调用时初始SingletonQ以后就不用再生成了?/p>
注意到lazy initialization形式中的synchronizedQ这?font color="#ff1493">synchronized很重?/font>Q如果没有synchronizedQ那么用getInstance()是有可能得到多个Singleton实例。关于lazy initialization的Singleton有很多涉及double-checked locking (DCL)的讨论,有兴者进一步研I?/p>
一般认为第一UŞ式要更加安全些?/font>
使用Singleton注意事项Q?br />
有时在某些情况下Q用Singletonq不能达到Singleton的目的,如有多个Singleton对象同时被不同的c装入器装蝲Q在EJBq样的分布式pȝ中用也要注意这U情况,因ؓEJB是跨服务器,跨JVM的?/p>
我们以SUN公司的宠物店源码(Pet Store 1.3.1)的ServiceLocatorZE微分析一下:
在Pet Store中ServiceLocator有两U,一个是EJB目录下;一个是WEB目录下,我们查这两个ServiceLocator会发现内容差不多Q都是提供EJB的查询定位服务,可是Z么要分开呢?仔细研究对这两种ServiceLocator才发现区别:在WEB中的ServiceLocator的采取Singleton模式QServiceLocator属于资源定位Q理所当然应该使用Singleton模式。但是在EJB中,Singleton模式已经失去作用Q所以ServiceLocator才分成两U,一U面向WEB服务的,一U是面向EJB服务的?/p>
Singleton模式看v来简单,使用Ҏ也很方便Q但是真正用好,是非怸ҎQ需要对Java的类 U程 内存{概忉|相当的了解?/p>
MQ?font color="#0000ff">如果你的应用Z容器Q那么Singleton模式用或者不用,可以使用相关替代技术?/font>
q一步深入可参考:
Double-checked locking and the Singleton pattern
When is a singleton not a singleton?
Builder模式是一步一步创Z个复杂的对象,它允许用户可以只通过指定复杂对象的类型和内容可以构建它?用户不知道内部的具体构徏l节.Builder模式是非常类似抽象工厂模?l微的区别大概只有在反复使用中才能体会到.
Z使用?
是ؓ了将构徏复杂对象?b>q程和它?i>部g解?注意: 是解?b>q程?i>部g.
因ؓ一个复杂的对象,不但有很多大量组成部?如汽?有很多部?车轮 方向?发动有各U小零g{等,部g很多,但远不止q些,如何这些部件装配成一辆汽?q个装配q程也很复杂(需要很好的l装技?,Builder模式是Z部件和l装q程分开.
如何使用?
首先假设一个复杂对象是由多个部件组成的,Builder模式是把复杂对象的创建和部g的创建分别开?分别用BuildercdDirectorcL表示.
首先,需要一个接?它定义如何创建复杂对象的各个部g:
public interface Builder { //创徏部gA 比如创徏汽R车轮 } |
用Director构徏最后的复杂对象,而在上面Builder接口中封装的是如何创Z个个部g(复杂对象是由q些部gl成?,也就是说Director的内Ҏ如何部件最后组装成成品:
public class Director { private Builder builder; public Director( Builder builder ) { } } |
Builder的具体实现ConcreteBuilder:
通过具体完成接口Builder来构建或装配产品的部?
定义q明它所要创建的是什么具体东?
提供一个可以重新获取品的接口:
public class ConcreteBuilder implements Builder { Part partA, partB, partC; } |
复杂对象:产品Product:
public interface Product { } |
复杂对象的部?
public interface Part { } |
我们看看如何调用Builder模式:
ConcreteBuilder builder = new ConcreteBuilder();
Director director = new Director( builder );
director.construct(); //部g的创?/font>
Product product = builder.getResult(); //对象Q品)的创?/font>
Builder模式的应?br />在Java实际使用?我们l常用到"?(Pool)的概?当资源提供者无法提供够的资源,q且q些资源需要被很多用户反复׃n?需要用池.
"?实际是一D内?当池中有一些复杂的资源?断肢"(比如数据库的q接?也许有时一个连接会中断),如果循环再利用这?断肢",提高内存用效?提高池的性能.修改Builder模式中DirectorcM之能诊断"断肢"断在哪个部g?再修复这个部?
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>