结构模式.docx
《结构模式.docx》由会员分享,可在线阅读,更多相关《结构模式.docx(28页珍藏版)》请在冰豆网上搜索。
结构模式
设计模式之Adapter(适配器)
定义:
将两个不兼容的类纠合在一起使用,属于结构型模式,需要有Adaptee(被适配者)和Adaptor(适配器)两个身份.
为何使用?
我们经常碰到要将两个没有关系的类组合在一起使用,第一解决方案是:
修改各自类的接口,但是如果我们没有源代码,或者,我们不愿意为了一个应用而修改各自的接口。
怎么办?
使用Adapter,在这两种接口之间创建一个混合接口(混血儿).
如何使用?
实现Adapter方式,其实"thinkinJava"的"类再生"一节中已经提到,有两种方式:
组合(composition)和继承(inheritance).
假设我们要打桩,有两种类:
方形桩圆形桩.
publicclassSquarePeg{
publicvoidinsert(Stringstr){
System.out.println("SquarePeginsert():
"+str);
}
}
publicclassRoundPeg{
publicvoidinsertIntohole(Stringmsg){
System.out.println("RoundPeginsertIntoHole():
"+msg);
}
}
现在有一个应用,需要既打方形桩,又打圆形桩.那么我们需要将这两个没有关系的类综合应用.假设RoundPeg我们没有源代码,或源代码我们不想修改,那么我们使用Adapter来实现这个应用:
publicclassPegAdapterextendsSquarePeg{
privateRoundPegroundPeg;
publicPegAdapter(RoundPegpeg)(this.roundPeg=peg;)
publicvoidinsert(Stringstr){roundPeg.insertIntoHole(str);}
}
在上面代码中,RoundPeg属于Adaptee,是被适配者.PegAdapter是Adapter,将Adaptee(被适配者RoundPeg)和Target(目标SquarePeg)进行适配.实际上这是将组合方法(composition)和继承(inheritance)方法综合运用.
PegAdapter首先继承SquarePeg,然后使用new的组合生成对象方式,生成RoundPeg的对象roundPeg,再重载父类insert()方法。
从这里,你也了解使用new生成对象和使用extends继承生成对象的不同,前者无需对原来的类修改,甚至无需要知道其内部结构和源代码.
如果你有些Java使用的经验,已经发现,这种模式经常使用。
进一步使用
上面的PegAdapter是继承了SquarePeg,如果我们需要两边继承,即继承SquarePeg又继承RoundPeg,因为Java中不允许多继承,但是我们可以实现(implements)两个接口(interface)
publicinterfaceIRoundPeg{
publicvoidinsertIntoHole(Stringmsg);
}
publicinterfaceISquarePeg{
publicvoidinsert(Stringstr);
}
下面是新的RoundPeg和SquarePeg,除了实现接口这一区别,和上面的没什么区别。
publicclassSquarePegimplementsISquarePeg{
publicvoidinsert(Stringstr){
System.out.println("SquarePeginsert():
"+str);
}
}
publicclassRoundPegimplementsIRoundPeg{
publicvoidinsertIntohole(Stringmsg){
System.out.println("RoundPeginsertIntoHole():
"+msg);
}
}
下面是新的PegAdapter,叫做two-wayadapter:
publicclassPegAdapterimplementsIRoundPeg,ISquarePeg{
privateRoundPegroundPeg;
privateSquarePegsquarePeg;
//构造方法
publicPegAdapter(RoundPegpeg){this.roundPeg=peg;}
//构造方法
publicPegAdapter(SquarePegpeg)(this.squarePeg=peg;)
publicvoidinsert(Stringstr){roundPeg.insertIntoHole(str);}
}
还有一种叫PluggableAdapters,可以动态的获取几个adapters中一个。
使用Reflection技术,可以动态的发现类中的Public方法。
设计模式之Bridge
Bridge定义:
将抽象和行为划分开来,各自独立,但能动态的结合.
为什么使用?
通常,当一个抽象类或接口有多个具体实现(concretesubclass),这些concrete之间关系可能有以下两种:
1.这多个具体实现之间恰好是并列的,如前面举例,打桩,有两个concreteclass:
方形桩和圆形桩;这两个形状上的桩是并列的,没有概念上的重复,那么我们只要使用继承就可以了.
2.实际应用上,常常有可能在这多个concreteclass之间有概念上重叠.那么需要我们把抽象共同部分和行为共同部分各自独立开来,原来是准备放在一个接口里,现在需要设计两个接口,分别放置抽象和行为.
例如,一杯咖啡为例,有中杯和大杯之分,同时还有加奶不加奶之分.如果用单纯的继承,这四个具体实现(中杯大杯加奶不加奶)之间有概念重叠,因为有中杯加奶,也有中杯不加奶,如果再在中杯这一层再实现两个继承,很显然混乱,扩展性极差.那我们使用Bridge模式来实现它.
如何实现?
以上面提到的咖啡为例.我们原来打算只设计一个接口(抽象类),使用Bridge模式后,我们需要将抽象和行为分开,加奶和不加奶属于行为,我们将它们抽象成一个专门的行为接口.
先看看抽象部分的接口代码:
publicabstractclassCoffee
{
CoffeeImpcoffeeImp;
publicvoidsetCoffeeImp(){
this.CoffeeImp=CoffeeImpSingleton.getTheCoffeImp();
}
publicCoffeeImpgetCoffeeImp(){returnthis.CoffeeImp;}
publicabstractvoidpourCoffee();
}
其中CoffeeImp是加不加奶的行为接口,看其代码如下:
publicabstractclassCoffeeImp
{
publicabstractvoidpourCoffeeImp();
}
现在我们有了两个抽象类,下面我们分别对其进行继承,实现concreteclass:
//中杯
publicclassMediumCoffeeextendsCoffee
{
publicMediumCoffee(){setCoffeeImp();}
publicvoidpourCoffee()
{
CoffeeImpcoffeeImp=this.getCoffeeImp();
//我们以重复次数来说明是冲中杯还是大杯,重复2次是中杯
for(inti=0;i<2;i++)
{
coffeeImp.pourCoffeeImp();
}
}
}
//大杯
publicclassSuperSizeCoffeeextendsCoffee
{
publicSuperSizeCoffee(){setCoffeeImp();}
publicvoidpourCoffee()
{
CoffeeImpcoffeeImp=this.getCoffeeImp();
//我们以重复次数来说明是冲中杯还是大杯,重复5次是大杯
for(inti=0;i<5;i++)
{
coffeeImp.pourCoffeeImp();
}
}
}
上面分别是中杯和大杯的具体实现.下面再对行为CoffeeImp进行继承:
//加奶
publicclassMilkCoffeeImpextendsCoffeeImp
{
MilkCoffeeImp(){}
publicvoidpourCoffeeImp()
{
System.out.println("加了美味的牛奶");
}
}
//不加奶
publicclassFragrantCoffeeImpextendsCoffeeImp
{
FragrantCoffeeImp(){}
publicvoidpourCoffeeImp()
{
System.out.println("什么也没加,清香");
}
}
Bridge模式的基本框架我们已经搭好了,别忘记定义中还有一句:
动态结合,我们现在可以喝到至少四种咖啡:
1.中杯加奶
2.中杯不加奶
3.大杯加奶
4.大杯不加奶
看看是如何动态结合的,在使用之前,我们做个准备工作,设计一个单态类(Singleton)用来hold当前的CoffeeImp:
publicclassCoffeeImpSingleton
{
privatestaticCoffeeImpcoffeeImp;
publicCoffeeImpSingleton(CoffeeImpcoffeeImpIn)
{this.coffeeImp=coffeeImpIn;}
publicstaticCoffeeImpgetTheCoffeeImp()
{
returncoffeeImp;
}
}
看看中杯加奶和大杯加奶是怎么出来的:
//拿出牛奶
CoffeeImpSingletoncoffeeImpSingleton=newCoffeeImpSingleton(newMilkCoffeeImp());
//中杯加奶
MediumCoffeemediumCoffee=newMediumCoffee();
mediumCoffee.pourCoffee();
//大杯加奶
SuperSizeCoffeesuperSizeCoffee=newSuperSizeCoffee();
superSizeCoffee.pourCoffee();
注意:
Bridge模式的执行类如CoffeeImp和Coffee是一对一的关系,正确创建CoffeeImp是该模式的关键,
Bridge模式在EJB中的应用
EJB中有一个DataAccessObject(DAO)模式,这是将商业逻辑和具体数据资源分开的,因为不同的数据库有不同的数据库操作.将操作不同数据库的行为独立抽象成一个行为接口DAO.如下:
1.BusinessObject(类似Coffee)
实现一些抽象的商业操作:
如寻找一个用户下所有的订单
涉及数据库操作都使用DAOImplementor.
2.DataAccessObject(类似CoffeeImp)
一些抽象的对数据库资源操作
3.DAOImplementor如OrderDAOCS,OrderDAOOracle,OrderDAOSybase(类似MilkCoffeeImpFragrantCoffeeImp)
具体的数据库操作,如"INSERTINTO"等语句,OrderDAOOracle是OracleOrderDAOSybase是Sybase数据库.
4.数据库(Cloudscape,Oracle,orSybasedatabaseviaJDBCAPI)
设计模式之Composite(组合)
板桥里人2002/04/27
Composite定义:
将对象以树形结构组织起来,以达成“部分-整体”的层次结构,使得客户端对单个对象和组合对象的使用具有一致性.
Composite比较容易理解,想到Composite就应该想到树形结构图。
组合体内这些对象都有共同接口,当组合体一个对象的方法被调用执行时,Composite将遍历(Iterator)整个树形结构,寻找同样包含这个方法的对象并实现调用执行。
可以用牵一动百来形容。
所以Composite模式使用到Iterator模式,和ChainofResponsibility模式类似。
Composite好处:
1.使客户端调用简单,客户端可以一致的使用组合结构或其中单个对象,用户就不必关系自己处理的是单个对象还是整个组合结构,这就简化了客户端代码。
2.更容易在组合体内加入对象部件.客户端不必因为加入了新的对象部件而更改代码。
如何使用Composite?
首先定义一个接口或抽象类,这是设计模式通用方式了,其他设计模式对接口内部定义限制不多,Composite却有个规定,那就是要在接口内部定义一个用于访问和管理Composite组合体的对象们(或称部件Component).
下面的代码是以抽象类定义,一般尽量用接口interface,
publicabstractclassEquipment
{
privateStringname;
//网络价格
publicabstractdoublenetPrice();
//折扣价格
publicabstractdoublediscountPrice();
//增加部件方法
publicbooleanadd(Equipmentequipment){returnfalse;}
//删除部件方法
publicbooleanremove(Equipmentequipment){returnfalse;}
//注意这里,这里就提供一种用于访问组合体类的部件方法。
publicIteratoriter(){returnnull;}
publicEquipment(finalStringname){this.name=name;}
}
抽象类Equipment就是Component定义,代表着组合体类的对象们,Equipment中定义几个共同的方法。
publicclassDiskextendsEquipment
{
publicDisk(Stringname){super(name);}
//定义Disk网络价格为1
publicdoublenetPrice(){return1.;}
//定义了disk折扣价格是0.5对折。
publicdoublediscountPrice(){return.5;}
}
Disk是组合体内的一个对象,或称一个部件,这个部件是个单独元素(Primitive)。
还有一种可能是,一个部件也是一个组合体,就是说这个部件下面还有'儿子',这是树形结构中通常的情况,应该比较容易理解。
现在我们先要定义这个组合体:
abstractclassCompositeEquipmentextendsEquipment
{
privateinti=0;
//定义一个Vector用来存放'儿子'
privateLsitequipment=newArrayList();
publicCompositeEquipment(Stringname){super(name);}
publicbooleanadd(Equipmentequipment){
this.equipment.add(equipment);
returntrue;
}
publicdoublenetPrice()
{
doublenetPrice=0.;
Iteratoriter=equipment.iterator();
for(iter.hasNext())
netPrice+=((Equipment)iter.next()).netPrice();
returnnetPrice;
}
publicdoublediscountPrice()
{
doublediscountPrice=0.;
Iteratoriter=equipment.iterator();
for(iter.hasNext())
discountPrice+=((Equipment)iter.next()).discountPrice();
returndiscountPrice;
}
//注意这里,这里就提供用于访问自己组合体内的部件方法。
//上面dIsk之所以没有,是因为Disk是个单独(Primitive)的元素.
publicIteratoriter()
{
returnequipment.iterator();
{
//重载Iterator方法
publicbooleanhasNext(){returni //重载Iterator方法
publicObjectnext()
{
if(hasNext())
returnequipment.elementAt(i++);
else
thrownewNoSuchElementException();
}
}
上面CompositeEquipment继承了Equipment,同时为自己里面的对象们提供了外部访问的方法,重载了Iterator,Iterator是Java的Collection的一个接口,是Iterator模式的实现.
我们再看看CompositeEquipment的两个具体类:
盘盒Chassis和箱子Cabinet,箱子里面可以放很多东西,如底板,电源盒,硬盘盒等;盘盒里面可以放一些小设备,如硬盘软驱等。
无疑这两个都是属于组合体性质的。
publicclassChassisextendsCompositeEquipment
{
publicChassis(Stringname){super(name);}
publicdoublenetPrice(){return1.+Price();}
publicdoublediscountPrice(){return.5+super.discountPrice();}
}
publicclassCabinetextendsCompositeEquipment
{
publicCabinet(Stringname){super(name);}
publicdoublenetPrice(){return1.+Price();}
publicdoublediscountPrice(){return.5+super.discountPrice();}
}
至此我们完成了整个Composite模式的架构。
我们可以看看客户端调用Composote代码:
Cabinetcabinet=newCabinet("Tower");
Chassischassis=newChassis("PCChassis");
//将PCChassis装到Tower中(将盘盒装到箱子里)
cabinet.add(chassis);
//将一个10GB的硬盘装到PCChassis(将硬盘装到盘盒里)
chassis.add(newDisk("10GB"));
//调用netPrice()方法;
System.out.println("netPrice="+Price());
System.out.println("discountPrice="+cabinet.discountPrice());
上面调用的方法netPrice()或discountPrice(),实际上Composite使用Iterator遍历了整个树形结构,寻找同样包含这个方法的对象并实现调用执行.
Composite是个很巧妙体现智慧的模式,在实际应用中,如果碰到树形结构,我们就可以尝试是否可以使用这个模式。
以论坛为例,一个版(forum)中有很多帖子(message),这些帖子有原始贴,有对原始贴的回应贴,是个典型的树形结构,那么当然可以使用Composite模式,那么我们进入Jive中看看,是如何实现的.
Jive解剖
在Jive中ForumThread是ForumMessages的容器container(组合体).也就是说,ForumThread类似我们上例中的CompositeEquipment.它和messages的关系如图:
[thread]
|-[message]
|-[message]
|-[message]
|-[message]
|-[message]
我们在ForumThread看到如下代码:
publicinterfaceForumThread{
....
publicvoidaddMessage(ForumMessageparentMessage,ForumMessagenewMessage)
throwsUnauthorizedException;
publicvoiddeleteMessage(ForumMessagemessage)
throwsUnauthorizedExceptio