适配器模式深入.docx
- 文档编号:29873362
- 上传时间:2023-08-03
- 格式:DOCX
- 页数:32
- 大小:135.92KB
适配器模式深入.docx
《适配器模式深入.docx》由会员分享,可在线阅读,更多相关《适配器模式深入.docx(32页珍藏版)》请在冰豆网上搜索。
适配器模式深入
结构型模式,顾名思义讨论的是类和对象的结构,它采用继承机制来组合接口或实现,或者通过组合一些对象,从而实现新的功能。
GoF23种设计模式中的结构型模式有7种,分别是适配器模式(Adapter)、装饰器模式(Decorator)、代理模式(Proxy)、外观模式(Facade)、桥接模式(Bridge)、组合模式(Composite)、享元模式(Flyweight)。
它们之间的关系如图12-1所示。
图12-1 各模式之间的关系
其中对象的适配器模式是各种模式的起源,是一种比较重要的适配器模式。
从箭头的方向可以看出各个模式的产生关系。
这7种模式的作用不同,分别如下。
Ø 适配器模式(Adapter):
将某个类的接口转换成客户端期望的另一个接口表示。
适配器模式可以消除由于接口不匹配所造成的类兼容性问题。
Ø 根据适配对象的不同,又分为3种类型:
Ø 类的适配器模式:
用于对类进行适配。
Ø 对象的适配器模式:
用于对对象进行包装。
Ø 接口的适配器模式:
用于对接口抽象化。
Ø 装饰器模式(Decorator):
向某个对象动态地添加更多的功能。
装饰器模式是除类继承外另一种扩展功能的方法。
Ø 代理模式(Proxy):
为其他对象提供一个代理以控制对这个对象的访问。
Ø 外观模式(Facade):
为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
Ø 桥接模式(Bridge):
将一个抽象与实现解耦,以便两者可以独立的变化。
Ø 组合模式(Composite):
把多个对象组成树状结构来表示局部与整体,这样用户可以一样地对待单个对象和对象的组合。
Ø 享元模式(Flyweight):
通过共享以便有效地支持大量小颗粒对象。
接下来将对7种常用结构型模式进行比较,并通过经典的代表实例来说明该如何使用各种结构型模式:
Ø 适配器模式—Iterator适配器(对象的适配器模式)、Enumeration适配器(对象的适配器模式)、AWT事件适配器(接口的适配器模式)、I/O字节流到字符流的适配器(对象的适配器模式)。
Ø 装饰器模式—I/O输入/输出流管道的装饰器模式、Sitemesh装饰器。
Ø 代理模式—Java动态代理机制。
Ø 桥接模式—JDBC桥DriverManager。
Ø 组合模式—AWT容器Container。
Ø 享元模式—数据库连接池。
下面来看详细的内容。
12.1 适配器模式(Adapter)
下面从以下几个方面来详细讲解适配器模式。
Ø 适配器模式的核心思想。
Ø 第一种:
类的适配器模式(对类进行适配)。
Ø 第二种:
对象的适配器模式(对对象进行包装)。
Ø 第三种:
接口的适配器模式(对接口抽象化)。
Ø 何时使用适配器模式。
Ø Java中的应用—Iterator适配器(对象的适配器模式)。
Ø Java中的应用—Enumeration适配器(对象的适配器模式)。
Ø Java中的应用—AWT事件适配器(接口的适配器模式)。
Ø Java中的应用—I/O字节流到字符流的适配器(对象的适配器模式)。
12.1.1 适配器模式的核心思想
适配器模式的核心思想:
把原有的接口转变成调用者期待的接口,从而使不同接口的类可以一起工作。
适配器中包含如下3个角色。
Ø 源角色Adaptee:
需要适配的目标类或接口。
Ø 目标角色Target:
所期望得到的接口。
Ø 适配器角色Adapter:
适配器类是本模式的核心,用来把源接口转换成目标接口,显然这一角色不可以是接口,而必须是具体类。
这3者角色之间的交互关系便组成了适配器模式的模型,如图12-2所示。
Adaptee类只有operation()方法,没有newoperation()方法,但是客户端又需要目标类同时拥有这两个方法,这时便可以新建一个接口Target,并提供一个中间环节Adapter类,Adapter类实现了Target接口,并继承自Adaptee,Adapter类的operation()方法重新封装了Adapter类的operation()方法,并同时实现了newoperation()方法,这便实现了适配的目的。
图12-2 适配器模式的模型
适配器也叫做包装器模式(Wrapper),根据适配对象的不同,又可以将适配器模式分为3种子类型。
Ø 类的适配器模式:
用于对类进行适配。
Ø 对象的适配器模式:
用于对对象进行包装。
Ø 接口的适配器模式:
用于对接口抽象化。
下面分别通过实例的形式来展示这3种模式的使用。
12.1.2 第一种:
类的适配器模式(对类进行适配)
第一种模式是类的适配器模式,它用来对目标类进行包装,如图12-3所示。
Ø Source类是具体的原始类,是待适配的对象,它拥有一个函数operation1()。
Ø Targetable是要适配的目标接口,它拥有一个与Source同样的接口函数operation1(),并提供了一个新的接口函数operation2(),该函数是要扩展的功能。
Ø Adapter是适配器类,它必须继承Source类,并实现Targetable接口,从而将Source的功能扩展到Targetable接口所具有的功能。
适配后的Source类,即可以通过调用Targetable接口来实现对Source类的操作,如图12-3所示。
图12-3 类的适配器模式
下面来看具体的实现。
(1)Source类的源代码如程序12-1所示,其默认的操作函数operation1()用来输出一个字符串。
程序12-1 源类Source.java
packagestructure.adapter;
/**
*@authorliuzhongbing
*源类
*/
publicclassSource{
publicvoidoperation1(){
System.out.println("原始类的方法");
}
}
(2)Targetable必须首先具备与Source相同的函数接口,这样它才能够实现Source功能,然后增加扩展函数operation2()。
其源代码如程序12-2所示。
程序12-2 目标接口Targetable.java
packagestructure.adapter;
/**
*@authorliuzhongbing
*目标接口
*/
publicinterfaceTargetable{
/**
*与源类相同的接口函数
*/
publicvoidoperation1();
/**
*新的接口函数,源类中没有
*/
publicvoidoperation2();
}
(3)Adapter的作用是将Source适配为Targetable,因此它必须继承Source类并实现Targetable接口,这样它便拥有了Source函数operation1()的功能,该函数还拥有了与Targetable同样的接口;同时,它必须实现Targetable的扩展函数,它往控制台输出了一个字符串。
其源代码如程序12-3所示。
程序12-3 适配器类Adapter.java
packagestructure.adapter;
/**
*@authorliuzhongbing
*适配器模式,继承源类,并实现目标接口
*/
publicclassAdapterextendsSourceimplementsTargetable{
/**
*实现目标类的新接口函数
*/
publicvoidoperation2(){
System.out.println("适配目标类后的方法");
}
}
(4)我们就可以创建一个Adapter类的对象,该对象属于Targetable,调用该对象的operation1()函数将会实现对Source函数的调用,调用operation2()函数将会实现对Adatper实现函数的调用,这样就实现了对类Source到Targetable的适配。
测试类源代码如程序12-4所示。
程序12-4 测试类AdapterTest.java
packagestructure.adapter;
/**
*@authorliuzhongbing
*按照目标接口来创建实例,并调用该接口的各个实现函数
*/
publicclassAdapterTest{
publicstaticvoidmain(String[]args){
//创建目标接口的类实例
Targetableobj=newAdapter();
//调用目标类的方法
obj.operation1();
obj.operation2();
}
}
运行该程序的输出为:
原始类的方法
适配目标类后的方法
这正是我们想要的结果:
把类Source按照接口Targetable进行调用。
12.1.3 第二种:
对象的适配器模式(对对象进行包装)
第二种模式是对象的适配器模式,它用来对目标对象进行包装,因此又叫做包装器模式,如图12-4所示。
Ø Source类是具体的原始类,是待包装对象的类,它拥有一个函数operation1()。
Ø Targetable是要适配的目标接口,它拥有一个与Source同样的接口函数operation1(),并提供了一个新的接口函数operation2(),该函数是要扩展的功能。
Ø Wrapper是包装器类,它与Adapter适配器不同,它不需要继承Source,但必须拥有一个Source类型的对象source,该对象在构造函数中被赋值。
在该包装器的operation1()函数中,需要调用source的operation1(),这样就实现了对原有对象的调用;同时扩展实现Targetable的operation2()函数。
包装后的Wrapper类,即可以通过调用Targetable接口来实现对Source类的操作,如图12-4所示。
图12-4 对象的适配器模式
以上的Source类和Targetable接口与第一种模式相同,唯一不同的是Wrapper的实现方式不同。
其源代码如程序12-5所示。
程序12-5 包装器类Wrapper.java
packagestructure.adapter;
/**
*@authorliuzhongbing
*包装器模式
*/
publicclassWrapperimplementsTargetable{
privateSourcesource;
/**
*取得源类对象
*/
publicWrapper(Sourcesource){
super();
this.source=source;
}
/**
*调用源类对象的方法
*/
publicvoidoperation1(){
source.operation1();
}
/**
*实现目标类的新接口函数
*/
publicvoidoperation2(){
System.out.println("包装目标类后的方法");
}
}
创建一个Source类的对象source,并根据该对象创建一个Wrapper包装器obj,该包装器对象属于Targetable,调用该对象的operation1()函数将会实现对Source函数的调用,调用operation2()函数将会实现对Wrapper实现函数的调用,这样就实现了对类Source到Targetable的包装。
测试类源代码如程序12-6所示。
程序12-6 测试类WrapperTest.java
packagestructure.adapter;
/**
*@authorliuzhongbing
*按照目标接口来创建实例,并调用该接口的各个实现函数
*/
publicclassWrapperTest{
publicstaticvoidmain(String[]args){
//创建源类对象
Sourcesource=newSource();
//创建source的包装类对象
Targetableobj=newWrapper(source);
//调用目标类的方法
obj.operation1();
obj.operation2();
}
}
运行该程序的输出为:
原始类的方法
包装目标类后的方法
输出的结果与第一种模式相同,因此效果是与第一种模式相同的,不同的是适配的方式不同。
12.1.4 第三种:
接口的适配器模式(对接口抽象化)
有时我们会在一个接口中定义多个接口方法,如果要实现该接口编写一个类,就必须为每一个接口方法编写实现代码,这显然会造成很大的浪费。
为了解决这个问题,可以使用第三种适配器模式—默认适配器。
它会为原有的接口类实现一个默认的抽象类,在该抽象类中编写每一个接口的默认实现,当我们需要编写一个具体类时,只需要继承自该抽象类,而不需要实现原有的接口。
并且,此时我们不需要实现所有的接口方法,只实现需要的函数即可。
如图12-5所示为接口的适配器模式。
Ø Sourceable是定义了多个接口函数的接口类。
Ø DefaultWrapper是一个抽象类,它实现了接口Sourcable,并为每一个接口函数提供了默认的实现。
依据DefaultWrapper就可以编写不同的实现,在实现中只需要重写部分待实现的函数,而不需要重写全部。
图12-5 接口的适配器模式
下面来看具体的实现。
(1)Sourcable类的源代码如程序12-7所示,它定义了两个操作函数。
程序12-7 源接口Sourcable.java
packagestructure.adapter;
/**
*@authorliuzhongbing
*源接口
*/
publicinterfaceSourcable{
publicvoidoperation1();
publicvoidoperation2();
}
(2)DefaultWrapper实现了Sourcable接口,并提供了其两个接口函数的实现,在该实现中可以什么也不做,目的只是为了给其子类提供一个默认的实现。
其源代码如程序12-8所示。
程序12-8 默认适配器类DefaultWrapper.java
packagestructure.adapter;
/**
*@authorliuzhongbing
*包装器模式
*/
publicabstractclassDefaultWrapperimplementsSourcable{
publicvoidoperation1(){}
publicvoidoperation2(){}
}
(3)SourceSub1继承自DefaultWrapper,由于DefaultWrapper的屏蔽作用,SourceSub1可以只重新实现自己关心的函数operation1(),它负责输出一个字符串。
其源代码如程序12-9所示。
程序12-9 源接口的实现子类SourceSub1.java
packagestructure.adapter;
/**
*@authorliuzhongbing
*源接口的实现类
*/
publicclassSourceSub1extendsDefaultWrapper{
publicvoidoperation1(){
System.out.println("源接口的一个实现子类Sub1");
}
}
(4)SourceSub2继承自DefaultWrapper,由于DefaultWrapper的屏蔽作用,SourceSub2可以只重新实现自己关心的函数operation2(),它负责输出一个字符串。
其源代码如程序12-10所示。
程序12-10 源接口的实现子类SourceSub2.java
packagestructure.adapter;
/**
*@authorliuzhongbing
*源接口的实现类
*/
publicclassSourceSub2extendsDefaultWrapper{
publicvoidoperation2(){
System.out.println("源接口的一个实现子类Sub2");
}
}
以上我们编写了两个实现SourceSub1和SourceSub2,下面分别创建一个对象,创建的对象都属于Sourcable。
然后分别调用它们的方法operation1()和operation2()。
其源代码如程序12-11所示。
程序12-11 测试类DefaultWrapperTest.java
packagestructure.adapter;
publicclassDefaultWrapperTest{
publicstaticvoidmain(String[]args){
Sourcablesource1=newSourceSub1();
Sourcablesource2=newSourceSub2();
source1.operation1();
source1.operation2();
source2.operation1();
source2.operation2();
}
}
运行该程序的结果如下:
源接口的一个实现子类Sub1
源接口的一个实现子类Sub2
从输出的结果可以看出,source1和source2仅仅在运行自身实现的函数时发生了作用,对于没有实现的函数则调用了默认适配器DefaultWrapper的默认函数,什么也没有输出。
12.1.5 何时使用适配器模式
从以上的讲解我们已经知道,如果需要将一个类变成另一个类时就可以使用适配器模式。
但是根据需求的不同,可以分别选用3种不同的子模式。
Ø 类的适配器模式:
当希望将一个类转换成满足另一个接口时,可以模仿Adapter的做法来构造一个新的适配器类,该类继承原有的类并实现新的接口即可。
Ø 对象的适配器模式:
当希望将一个对象转换成另一个接口时,可以模仿Wrapper的做法来构造一个新的包装类,该类调用原有的类并实现新的接口即可。
Ø 默认适配器模式:
当不希望实现一个接口的所有方法时,可以模仿DefaultWrapper的做法构造一个抽象类,给出所有方法的默认的实现,这样,从这个抽象类再继承下去的子类就不必实现所有的方法了。
以上的说法稍显抽象,在JavaAPI中有许多地方都运用了适配器模式,下面我们找出来分析一下,你就会明白适配器模式的实际用处了。
12.1.6 Java中的应用—Iterator适配器(对象的适配器模式)
在Java中存在两个迭代器类:
Iterator和Enumeration。
两者都可以由相应的集合对象转化而来,例如ArrayList可以变成Iterator对象,Vector可以变成Enumeration对象。
然而,有时你可能需要将ArrayList转换成Enumeration对象,这就需要使用包装器模式来将Iterator对象转换成Enumeration对象了。
此时我们可以选择第二种适配器—对象的适配器。
如图12-6所示,包装器Itermeration负责将对象Iterator改造成Enumeration的形式。
图12-6 Iterator适配器
Iterator对象拥有如下两个函数:
Ø hasNext()判断是否有下一个对象。
Ø next()取得下一个对象。
为了将Iterator转换为Enumeration,此时可以调用上面的两个函数来分别实现Enumeration的如下两个函数:
Ø hasMoreElements()判断是否有下一个对象。
Ø nextElement()取得下一个对象。
其完整的代码如程序12-12所示。
程序12-12 Iterator被包装为Enumeration类型Itermeration.java
packagestructure.adapter;
importjava.util.Iterator;
importjava.util.NoSuchElementException;
importjava.util.Enumeration;
/**
*@authorliuzhongbing
*使用了包装器模式
*/
publicclassItermerationimplementsEnumeration
Iterator
publicItermeration(Iterator
this.it=it;
}
publicbooleanhasMoreElements(){
returnit.hasNext();
}
publicObjectnextElement()throwsNoSuchElementException{
returnit.next();
}
}
下面来编写测试代码。
首先创建一个ArrayList对象并初始化数据,进而得到Iterator对象,然后根据该对象构造Itermeration的对象即可,最后即可按照Enumeration的接口进行迭代。
其源代码如程序12-13所示。
程序12-13 测试类ItermerationTest.java
packagestructure.adapter;
importjava.util.ArrayList;
importjava.util.Enumeration;
impo
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 适配器 模式 深入