`

三十二:适配器模式

阅读更多
适配器模式把一类的接口变成客户端所期待的另一个接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作.

一:名字的由来
这很像变压器(Adapter),变压器把一种电压变成另一种电压。把美国的电器拿回中国大陆用的时候,用户就面临电压不同的问题。美国的生活用电电压是110V,而中国的电压是220V,如果要在中国使用美国的电器,就必须有一个能把220V电压转换成110V电压的变压器,而这正是本模式所做的事。

前面提到适配器模式有两种,下面一一介绍:

二:类的适配器模式
类的适配器模式把被适配的类的API转换成为目标类的API,此模式所涉及的角色有:
(A)目标(Target)角色:这就是所期待得到的接口(注:由于这里讨论的是类适配器模式,因此目标不可以是类)
(B)源(Adaptee)角色:现在需要适配的接口
(C)适配器(Adapter)角色:适配器类是本模式的核心,它把源接口转换成目标接口,显然这一角色不可以是接口,而必须是具体类
源代码如下:
package cai.milenfan.basic.test; 
public interface Target { 
//这是源类也有的方法
void sampleOperation1(); 
//这是源类不有的方法
void sampleOperation2(); 
} 

package cai.milenfan.basic.test; 
public class Adaptee { 
public void sampleOperation1(){ 
//....your code 
} 
} 

package cai.milenfan.basic.test; 
public class Adapter extends Adaptee implements Target{ 
//由于源类没有这个方法,因此适配器类补充这个方法
public void sampleOperation2() { 
} 
} 

由上知,Adatee类并没有sampleOperation2()方法,而客户端则期待这个方法,为使客户端能够使用Adaptee类,提供一个中间五一节,即类Adapter,把Adaptee的API与Target类的API衔接起来,Adapter与Adaptee是继承关系,这决定了这个适配器是类.
效果分析:
a.使用一个具体类把源(Adaptee)适配到目标(Target)中,这样一来,如果源以及源的子类都使用此类适配,就行不通了。
b.由于适配器类是源的子类,因此可以在适配器类中置换掉(Override)源的一些方法
c.由于只引进了一个适配器类,因此只有一个路线到达目标类,使问题得到简化.

三:对象的适配器模式
与类的适配器模式一样,对象的适配器模式把被适配的类的API转换成目标类的API,与类的适配器不同的是,对象的适配器不是使用继承关系连接到Adaptee类,而是使用委派关系连接到Adaptee类,它所需要的角色和类的适配器模式相同,下面是源代码:
package cai.milenfan.basic.test; 
public interface Target { 
//这是源类也有的方法
void sampleOperation1(); 
//这是源类不有的方法
void sampleOperation2(); 
} 


package cai.milenfan.basic.test; 
public class Adaptee { 
public void sampleOperation1(){ 
//....your code 
} 
} 

package cai.milenfan.basic.test; 
public class Adapter implements Target{ 
private Adaptee adaptee; 
public Adapter(Adaptee adaptee){ 
super(); 
this.adaptee = adaptee; 
} 
//源类有方法,适配器直接委派即可
public void sampleOperation1() { 
adaptee.sampleOperation1(); 
} 
//源类没有这个方法,因此适配器类需要补充此方法
public void sampleOperation2() { 
//write your code here..... 
} 
} 


请不要误认为适配器模式就是为了补充源角色没有的方法而准备的哦! !
效果分析:
a.一个适配器可以把多种不同的源适配到同一个目标,换言之,同一个适配器可以把源类和它的子类都适配到目标接口
b.与类的适配模式相比,要想置换源类的方法就不容易。如果一定要置换源类的一个或多个方法,就只好先做一个源类的子类,将源类的方法置换掉,然后再把源类的子类当做真正的源进行适配。
c.虽然要想置换源类的方法不容易,但是要想增加一些新的方法则方便得很,而且新增的方法可同时适用于所有的源.

四:在什么情况下使用适配器模式
(1)系统需要使用现有的类,而此类的接口不符合系统的需要。
(2)想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类
(3)(对对象的适配器模式而言)在设计里,需要改变多个已有的子类的接口,如果使用类的适配器模式的话,就要针对每一个子类做一个适配器类,而这不太实际.

五:利用适配器模式指方(Cube)为圆(BallIF)
package cai.milenfan.basic.test; 
public class Cube { 
private double width; 
public Cube(double width){ 
this.width = width; 
} 
//计算体积
public double calculateVolume(){ 
return width*width*width; 
} 
//计算面积
public double calculateFaceArea(){ 
return width*width; 
} 
public double getWidth() { 
return width; 
} 
public void setWidth(double width) { 
this.width = width; 
} 
} 

package cai.milenfan.basic.test; 
public interface BallIF { 
//计算面积
double calculateArae(); 
//计算体积
double calculateVolume(); 
//半径的get/set 
double getRadius(); 
void setRadius(double radius); 
} 


package cai.milenfan.basic.test; 
public class MagicFinger implements BallIF{ 
private double radius = 0; 
private static final double PI = 3.14d; 
private Cube adaptee; 
public MagicFinger(Cube adaptee){ 
super(); 
this.adaptee = adaptee; 
radius = adaptee.getWidth(); 
} 
public double calculateArae() { 
return PI * 4.0d * (radius * radius); 
} 
public double calculateVolume() { 
return PI * 4.0d/3.0d * (radius * radius * radius); 
} 
public double getRadius() { 
return radius; 
} 
public void setRadius(double radius) { 
this.radius = radius; 
} 
} 


上面的MagicFinger接收一个正方体,返回此正方体的内切球,本例用的是对象的适配器模式,(相对于类的适配模式)这样做的好处是,如果一旦决定不仅要支持正方体,而且要支持四面体等多面体,可以使用同一个MagicFinger类,而不必对每一个多面体建立一个MagicFinger类。

六:适配器模式在层次上的应用
(1)wine是一个免费软件,它允许用户在Linus环境里运行Window程序,可以知道,Wine提供了从Linux到Window图形界面的适配器,它使得Windows的图形界面可以在Linux界面里运行,这样不必修改任何一个图形界面,便可以使两者兼容。
(2)jdbc驱动软件
JDBC给出了一个客户端通用的界面,每一个数据库引擎的JDBC驱动软件都是一个介于JDBC接口和数据库引擎接口之间的适配器软件,如下图:

<--------------------Oracle JDBC------------Oracle
------------------
JDBC <--------------------SQLServer JDBC--------------SQLServer
------------------
<--------------------MySQL JDBC--------------MySQL
抽象的JDBC接口和各个数据库引擎的API之间都需要相应的适配器软件,即为数据库引擎准备的驱动软件.
(3)jdbc/odbc桥梁
java application-->jdbc driver manager-->jdbc/odbc bridge-->odbc driver-->DB
jdbc的库不可能和odbc的库有相同的接口,因此使用适配器模式将odbc的API接口改为jdbc的接口就是唯一可行的办法,因此jdbc/odbc bridge是适配器模式的应用.

七:模式实现的讨论
本模式需要注意的地方有:
(1)目标接口可以省略,此时目标接口和源接口实际上是相同的,由于源是一个接口,而适配器是一个类,因此这种做法看似平庸而并不平庸,它可以使客户端不必实现不需要的方法(这将在缺省适配的情况下看到)
(2)适配器类可以是抽象的(这将在缺省适配的情况下看到)
(3)带参数的适配器模式,使用这种办法,适配器可以根据参数返回一个合适的实例给客户端.

八:适配器模式和相关的模式(跳过)
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics