设计模式学习之Adapter模式
今天要说适配器模式了,适配器模式其实非常好理解,因为现实中存在很多不同种类的适配器,像usb转串口线就是讲usb接口和串口进行转换,VGA转HDMI线就是讲VGA接口和HDMI接口进行转换,这些都是硬件层次的,那么软件层次的就更好举例了,比如现在有某些usb设备,像usb无线键盘/鼠标啊,usb网卡等等如果直接插到电脑上是不会工作的,必须在电脑端还要装上相应的驱动程序,那么这个驱动程序其实就是一个软件适配器,将usb设备转换成为操作系统能够识别的设备,其实,usb设备远不止这些,到淘宝一查,我来个去,真是什么都有啊:
其实这些设备不是都有适配器的,有很多只用使用usb进行供电,把usb当电源了,但有些数码设备是确实有适配器的,很简单的就是手机的充电线,现在的智能手机都有一根充电线和一个充电器,充电线的一头是连接手机的,另一头则是一个usb接口,这条线连接到充电器就能给手机充电,连接到电脑就能使电脑和手机之间传输数据。
其实很早以前,就连鼠标键盘这种最常见的设备,都不是用的usb接口,而是用的PS/2接口,但现在支持usb接口的设备是越来越多啊,姑且不论usb到底为什么这么受欢迎(其实就是方便 ),我们只看这么多设备是如何做到支持usb接口的,难道说他们都会升级固件?那恐怕不尽然,除非是在usb接口普及之后所生产的新产品,否则那些旧产品要支持新的接口是不现实的,因为是硬件,厂商不可能回收后重做再给用户啊,那多亏啊。那这么说,那些旧产品就不能使用usb接口了啊,那usb接口怎么可能普及的这么快呢?答案是:适配器,不管是硬件的,还是软件的(驱动程序),都能
使采用旧接口的设备使用新的接口。
就比如usb网卡,一个usb网卡其实内部包含了一个usb接口与网卡的转接器,而在电脑端也有相应的网卡驱动让系统识别这是个网卡,它就有硬件和软件两个适配器,当用户需要从网络中请求数据时,操作系统会将请求编码后通过驱动程序从usb接口转发给网卡,网卡中的适配器对收到的请求进行解码后发送给网卡处理,网卡从互联网获得数据后将数据通过网卡适配器进行编码后通过usb接口发给电脑,电脑端操作系统通过驱动程序将数据再次解码后交给用户,这是不是有些复杂?
哎,扯了这么多,还没进入正题呢,既然适配器适用于不同接口之间的转换,那么在设计模式中是如何做到的呢?
还是照本宣科,先把定义摆出来:
适配器模式:
将一个类的接口转换成客户希望的另外一个接口。Adapter 模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
适用性:
- 你想使用一个已经存在的类,而它的接口不符合你的需求。
- 你想创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作。
- (仅适用于对象Adapter )你想使用一些已经存在的子类,但是不可能对每一个都进行子类化以匹配它们的接口。对象适配器可以适配它的父类接口。
定义总是概括性太强,不适合理解啊,有点懵了吧,下面来看个例子吧,就拿usb网卡来说,比如要做一个usb网卡驱动,操作系统提供的网卡API为:
public interface INetCard { public Host connect(IPAddr ip); public NetData getData(Host host); public void sendData(Host host, NetData data); }
而usb接口提供的API为:
public interface IUsb { public UsbData getData(); public sendData(UsbData data); }
网卡端的解码的方式为:
public class NetCardDecoder { private UsbData data; public void NetCardDecoder(data) { this.data=data; } public void doWork(Operator operator) { operator.execute(); } public Operator decode(UsbData data) { //对USB接口获取的数据进行解析后对不同的请求返回不同的操作对象 } }
现在我们要做网卡驱动,所以需要将网卡接口适配成Usb接口,所以我们创造一个网卡适配器:
//网卡数据格式与Usb数据格式的转换工具类 public class DataConvertorTool { //将UsbData转换NetData public static NetData usbData2Net(UsbData data) { } //将NetData转换UsbData public static UsbData netData2Usb(NetData data) { } } //网卡适配器类 public class NetCardAdapter implements INetCard { private IUsb usb; public class NetCardAdapter(IUsb usb)//将被适配者传给适配器 { this.usb=usb; } //适配网卡接口 public Host connect(IPAddr ip) { UsbData data=new UsbData("connect"); data.addParam("ip", ip); usb.sendData(data); } public NetData getData(Host host) { UsbData dataRequest=new UsbData("get"); data.addParam("host", host); usb.sendData(data); UsbData dataUsbReceive=usb.getData(); NetData dataNetReceive=DataConvertorTool.usbData2Net(dataUsbReceive); return dataNetReceive; } public void sendData(Host host, NetData data) { UsbData dataRequest=new UsbData("send"); dataRequest.addParam("host", host); dataRequest.addParam("data",data); usb.sendData(data); } }
从上述代码可以看出,适配器通过实现用户接口,并通过组合被适配者接口来实现接口之间的转换,但上述代码中我们只是实现了适配器的一种方式,即用组合的方式实现适配,其实,适配器模式还能使用另外一种方式,即类的多继承,但java中无法实现多继承,所以无法实现,但java支持多接口的继承,所以我个人认为其实概念上也是可以实现的,如果使用第二种方式,可以这样来写适配器:
//网卡适配器类 public class NetCardAdapter implements INetCard, IUsb { private IUsb usb; public class NetCardAdapter(IUsb usb)//将被适配者传给适配器 { this.usb=usb; } //适配网卡接口 public Host connect(IPAddr ip) { UsbData data=new UsbData("connect"); data.addParam("ip", ip); usb.sendData(data); } public NetData getData(Host host) { UsbData dataRequest=new UsbData("get"); data.addParam("host", host); usb.sendData(data); UsbData dataUsbReceive=usb.getData(); NetData dataNetReceive=DataConvertorTool.usbData2Net(dataUsbReceive); return dataNetReceive; } public void sendData(Host host, NetData data) { UsbData dataRequest=new UsbData("send"); dataRequest.addParam("host", host); dataRequest.addParam("data",data); usb.sendData(data); } //继承了Usb接口 public UsbData getData() { } public sendData(UsbData data) { } }
但由于只能继承接口,所以对于Usb接口方法还必须重新实现,显然是不现实的,因为这样就不能实现代码的复用和封闭了,自然是没有意义的,但若在c++或其他支持多继承的语言中,通过多继承被适配器类而不是接口可以方便的实现代码复用。
适配器模式与装饰者模式有些类似,它们都是结构型设计模式,然后都是对某个接口进行操作,但它们却有本质的不同:
首先, 适配器模式是将一个接口转换为另一个接口,而装饰者模式并不改变接口,只是在原实现上面动态的添加某种职责 。
其次, 它们的引用对象不一样,适配器模式引用被适配者(属于另一个接口),而装饰者模式引用自身(同一个接口) 。
最后, 它们的使用方式不一样,装饰者模式通过product=new ProductDecorator(product)来包装自身,而适配器模式通过product=new ProductAdapter(otherProduct)来像product一样使用otherProduct 。
作者:everdom
出处:http://everdom.cnblogs.com/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利。