设计模式之适配器模式

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

第一点比较重要的是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的一部分功能,

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

其情况就像下面的图


代码部分:

public
class 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,我们就可以只复写自己需要的方法,而不需要把所有的方法都列出来。当然有一点别的好处是。抽象类可以写

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

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

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



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值