设计模式之适配器模式

来源:互联网 发布:气球兵升级数据 编辑:程序博客网 时间:2024/05/21 10:04

对于设计模式,是长期代码的一种优化,我结合一些实例,,以自己的见解讲解一下我的浅识。说到适配器模式,

第一点比较重要的是uml图的理解,也许你现在不理解代码,但是理解了uml图,对于你以后的认识会有很大帮助。

适配器模式主要分为类适配器,对象适配器和缺省适配器。

现在我们看一下类适配器的uml结构图

上面可以看出,适配器模式包括三部分Target(目标),Adaptee(源),Adapter(适配器),在这里的我简单的写下代码,让大家有一个直观的认识

Target

public interface Target {    public  void operation1();public  void operation2();}

Adaptee

public class Adaptee {    public void operation1(){System.out.println("我是操作1哦");}}

Adapter

public class Adapter extends Adaptee implements Target{public void operation2() {}    }
但是从上面我们除了能看到复用了一些代码外,几乎看不到任何别的意思了,这样的感觉很不好,所以现在我们就构造一个经典的场景,

中国的电器工作电压是220v的,而美国的电器工作电压是110v,现在我们在美国买了电器要在中国用,那么怎么办呢?我们需要一个变压器,

因此适配器模式有了另一个名字,变压器模式,或者转换器模式。

那么代码如何进行表示呢?下面给出实例。

中国电压

public class ChinaStandard {    public void useCnSta(){System.out.println("我们能使用220V的电压");}}

能使用中国的美国制式电压

public interface AmericaEle {    public void useAmSta();}


美国电压

public class AmericaStandard implements AmericaEle{public void useAmSta() {System.out.println("我们只能使用使用110v");}}


变压器

public class Transform extends ChinaStandard implements AmericaEle{    //使用中国220v电压    public void useAmSta() {                this.useCnSta();            }}
我们通过变压器,能够使用220v的中国电压。哈哈,不难吧。

从上面的场景不难看出我们的适配器模式在于复用代码,(这不废话么),但是我们复用代码的前提是什么呢?

复用代码的前提是在不破坏原有代码的继承结构的基础上,并且不对代码内部的结构进行破坏,也就是我们所说的

为了某个目的,进行暂时性复用某个类的方法,既然是暂时性的,我们不能破坏原来的部分,这个不好理解,我会以jdk的io流为大家

详细介绍为什么。

我们上面的代码对于单一的类能够很好的使用了,但是如果我们的源如果有很多的派生类,但是我们的继承只能继承一个,我们没办法了,

所以我们的对象适配器横空出现了。下面是我们对象适配器的uml:


那么以这个方式,我们对原来的变压器进行优化,就变成了我们的对象适配器变压器

public class Transform implements AmericaEle{    private ChinaStandard chinaStandard=null;        public Transform(ChinaStandard chinaStandard){        this.chinaStandard=chinaStandard;    }        public void useAmSta() {                chinaStandard.useCnSta();            }}

从这个代码上我们可以看出,我们只需要定义一个父类,通过构造函数传进去,我们就可以对多个继承关系的源进行适配了。

我们遇到的情况不会像上面一样简单,但是通过变压器的例子我们可以看出,我们的适配器是为了兼容而生的,所以他大多数用的

时候是系统升级的时候,或者要同时使用二个类的功能,而这两个类都有自己的体系,我们破坏将会影响一系列的问题,说了这么多,

意思没有变,那么我们看下复杂的情况。

那么拿我们java io的结构来说吧。我们java io的体系图:


看上去是比较复杂的,也是很让大家头疼的,那么我们一点点的说:

我们的顶级InputStream是一个抽象类,他又自己的流式功能,但是我们的InputStream现在需要一个处理文件的功能,

但是我们处理文件的功能和流式功能是兼容性不好的,也许你会直接说,我们可以直接在InputStream里加入File的处理么?

但是如果贸然加入一些方法,我们在扩展别的功能处理时就会增加InputStream的复杂性,而我们需要的也只是File的一部分功能,

如果继承的话,不仅会破坏原有的体系而且会把我们不用的功能加进来。那么在处理这个问题时,我们用到了适配器模式,

其情况就像下面的图


代码部分:

publicclass FileInputStream extends InputStream{    /* File Descriptor - handle to the open file */    private FileDescriptor fd;    private FileChannel channel = null;    public FileInputStream(FileDescriptor fdObj) {        SecurityManager security = System.getSecurityManager();        if (fdObj == null) {            throw new NullPointerException();        }        if (security != null) {            security.checkRead(fdObj);        }        fd = fdObj;    }}

具体的方式就是用了对象适配器,就像我们有了StringBufferInputStream,但是我们现在需要文件流,必然和StringBufferInputStream不兼容,就跟中国电压和美国电压一样,虽然形式上不完全一样,但是思想上是一样的。StringBufferInputStream和ByteArrayInputStream以及InputStreamReader等等都是用的适配器,因为不知道现有的那个流。

这里我们不详细说了,那么我们看一下缺省适配器,其uml:



缺省适配器在java中的使用比较好的是WindowAdapter,我们怎么理解呢?

看一下代码结构:

public interface WindowListener extends EventListener {      public void windowOpened(WindowEvent e);       public void windowClosing(WindowEvent e);        public void windowClosed(WindowEvent e);    public void windowIconified(WindowEvent e);        public void windowDeiconified(WindowEvent e);      public void windowActivated(WindowEvent e);      public void windowDeactivated(WindowEvent e);}
如果我们实现WindowListener我们必须写出所有方法,但是我们现在有了WindowAdapter,他是一个抽象类,代码结构

public abstract class WindowAdapter implements WindowListener{        public void windowOpened(WindowEvent e);       public void windowClosing(WindowEvent e);        public void windowClosed(WindowEvent e);    public void windowIconified(WindowEvent e);        public void windowDeiconified(WindowEvent e);      public void windowActivated(WindowEvent e);      public void windowDeactivated(WindowEvent e);}

我只写了部分,但是如果我们实现WindowAdapter,我们就可以只复写自己需要的方法,而不需要把所有的方法都列出来。当然有一点别的好处是。抽象类可以写

默认的实现,而我们的接口不行,而有时候默认的行为是很有用的。

写到这里,本文已经差不多要完了,有很多准备了很久,但是写出来改了又改,我在努力从深一点的方式和大家探讨问题,我相信我会越来越熟练

这种方式。在这里我希望大家把我的错误说出来,代码思想本来就是分享的,为了不误人子弟,有错误一定要和我说,在此献上感谢,笔者和大家都正在这条路上奋斗。



0 1
原创粉丝点击