JAVA设计模式(四)
---------适配器模式
前言
前三篇文章主要介绍了,Java中三种常用的创建模式,从这一篇开始我们来探讨一下Java的结构模式。
为了更好的理解适配器模式,我们先用生活中的一个例子作引:
某天,我想重新组装一台电脑,因为囊中羞涩,某些配件还想使用原来家里那太“老爷车”的部件,比如鼠标吧,那时的鼠标都是P/S口的,可现在我组装的新电脑的主板已经找不到P/S接口了,怎么办?没关系,找一个P/S接口和USB接口的转接器(比鼠标便宜点,呵呵),问题就解决了,这转接器正是我们今天要谈到的适配器。很明显在软件开发中采用类似于上面方式的编码技巧被称为适配器模式,主要目的也和现实中差不多,就是为了复用。
定义和结构
配器模式定义
将一个类的接口转换成客户希望的另外一个接口,属于结构型模式,需要有Adaptee(被适配者)和Adaptor(适配器)两个身份.可能你还是不太明白为什么要使用适配器模式。我们来举个程序的例子也许能更直接的解除你的疑惑……
比如,在一个画图的小程序中,你已经实现了绘制点、直线、方块等图形的功能。而且为了让客户程序在使用的时候不用去关心它们的不同,还使用了一个抽象类Shape来规范这些图形的接口。现在你要来实现文本框输入的绘制,这相对与普通的图元就显得复杂了,既需要相应的组件支持还需要对输入文字进行处理,这时你发现另一个第三方JAR包已经有了一个名为 TextView 的类有了该功能的实现。在你庆幸之余,发现并没有TextView中对外提供方法和你在抽象类中规定的方法名称或者结构不一样,并且我们我们没有这个JAR包的源代码,我们不能修改!这可怎么办?我们去寻找这个开源JAR包的源码?即使我们得到了这些源代码,修改 TextView 也是没有什么意义的,因为不应该仅仅为了实现一个应用,就把代码修改成特定领域相关的接口,这样也是不利于以后复用的。
那么,还有其它的方法吗?那就是适配器模式了。我们可以定义一个TextShape类,由它来适配TextView的接口和Shape的接口。我们可以用两种方法做这件事: 1) 继承Shape类的接口和TextView的实现,或2) 将一个TextView实例作为TextShape的组成部分,并且使用TextView的接口实现TextShape。这两种方法恰恰对应于Adapter模式的类和对象版本。我们将TextShape称之为适配器Adapeter。由于在Java 中不支持多重继承,而且继承有破坏封装之嫌,众多的书中都提倡使用组合来代替继承。因此这里我们就不再对类适配器模式进行介绍。下面有关例子都是组合模式是适配器的应用。
配器模式结构
以上面绘图程序为例我们来看看适配器模式的组成吧。
目标(Target)角色
定义Client 使用的接口,在我们例子中就是定义的抽象类Shape的drawShape()方法。
/** * 客户端接口---Target角色 * @author Administrator * */ public interface Shape { public void drawShape();
} |
我们在系统中已经实现的子类图形:
public class Circle implements Shape { @Override public void drawShape() { System.out.println("我画了一个圆形"); } } |
public class Line implements Shape { @Override public void drawShape() { System.out.println("我画了 一条直线 "); } } |
public class Rectangle implements Shape { @Override public void drawShape() { System.out.println("我画了 一个长方形 "); } } |
被适配(Adaptee)角色
这个角色有一个已存在并使用了的接口,而这个接口是需要我们适配的。在例子中就是那个第三方JAR包里面的一个类,我们模拟为一个叫TextView的类,对外提供名字为drawTextView(String text)的方法。
public class TextView { /** * 模拟第三方jar文件中的类 * @param text * 文本框文字 */ public void drawTextView(String text) { System.out.println("我复用了一个文本框实现!内容是:" + text); } } |
适配器(Adapter)角色
这个适配器模式的核心。它将被适配角色已有的接口转换为目标角色希望的接口。
/** * 对象适配器 * * @author Administrator * */ public class TextShape implements Shape { private TextView view; public TextShape(TextView view) { this.view = view; } @Override public void drawShape() { // TODO Auto-generated method stub String text = "测试文本 "; view.drawTextView(text); } }
|
测试代码 public class MyTest { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub Shape s=new Circle(); s.drawShape();
s=new Line(); s.drawShape();
s=new Rectangle(); s.drawShape();
TextView view=new TextView(); s=new TextShape(view); s.drawShape(); } }
打印结果:
我画了 一个圆形 我画了 一条直线 我画了 一个长方形 我复用了一个文本框实现!内容是:测试文本 |
总结
可以看出使用适配器模式是为了在面向接口编程中更好的复用。如果你的系统中没有使用到面向接口编程,没有使用到多态,我想大概也不会使用到适配器模式。
适配器模式对角色定义非常重要,一般来说系统中的Target都为抽象的多态接口,Adaptee被适配角色都为其他系统或模块的接口,适配器一般都是我们自定义实现Target抽象接口,并且在内部引用Adaptee对象,在适配接口中,调用Adaptee原有分方法,从而实现适配。从最表层的概念来说就是Target和Adaptee通过Adapter连接了。