• 设计模式适配器


    adapter模式:适配老版本接口 和 第三方接口

    场景介绍:首先举一个场景,这个场景其实还是挺常见的,因为你的系统不断的在迭代,

    (1) 假设,我们做了一个第一版的系统,这个系统里有一个接口 和一个实现类;

    (2) 接着我们开始做第二版的系统,这个系统我们定义了一个新的接口,和新的实现类;

    (3) 但是我们同时希望能够基于第二版的接口,或者说我们同时在第二版的系统中 也要使用第一版系统中定义的那个老接口和老实现类

     现在如果不用模式的话,我们写了一套代码,然后,这套代码里面,我们又要用新版本的接口,又要用老版本的接口,这个时候,我们的代码看起来是什么样子的,如下:

    public class WithoutAdapterPatternDemo {
    
        public static void main(String[] args) {
            OldInterface oldObject = new OldInterfaceImpl();
            NewInterface newObject = new NewInterfaceImpl();
            oldObject.oldExecute();
            newObject.newExecute();
        }
    
        /** 老版本接口 */
        public static interface OldInterface{
            void oldExecute();
        }
    
        /** 老版本接口的实现类 */
        public static class OldInterfaceImpl implements OldInterface{
            @Override
            public void oldExecute() {
                System.out.println("老版本接口实现的功能逻辑");
            }
        }
    
        /** 新版本接口 */
        public static interface NewInterface{
            void newExecute();
        }
    
        /** 新版本接口的实现类 */
        public static class NewInterfaceImpl implements NewInterface{
            @Override
            public void newExecute() {
                System.out.println("新版本接口实现的功能逻辑");
            }
        }
    
    }

    先定义一个老版本的接口OldInterface,老版本接口,在这个OldInterface里面,给它一个方法,我们叫做oldExecute();

    然后是老版本的实现类:OldInterfaceImpl,重写接口中的方法,OldExecute(),方法里,我们就直接打印一行输出就行了:老版本接口实现的功能逻辑 

    然后同样,新版本接口NewInterface,新版本接口的实现类NewInterfaceImpl,接口中的方法newExecute(),实现类中的方法也同样打印输出一下:新版本接口实现的功能逻辑;

    再写一个main方法,然后

    OldInterface oldObject = new OldInterfaceImpl();

    NewInterface newObject = new NewInterfaceImpl();

    oldObject.oldExecute();

    newObject.newExecute();

    这个代码就写完了,执行一下,输出如下:

    老版本接口实现的功能逻辑

    新版本接口实现的功能逻辑

    这里说一下,如果不用任何设计模式,我们的问题在哪儿?问题其实很明显,就是说,我们的新的代码中,融合了新老两套接口,这个是很麻烦的一个事情;

    首先,如果你这么干的话,就会导致代码很恶心,因为面向的是规范和风格完全不同的两套接口,然后你的理解和维护的成本就提高了。

    其次,假如说,现在都不给你选择使用老版本接口的机会,直接就是,强制性公司规范,要求按照新版本接口来走,你的老版本接口的实现类,就没法用了啊?

    难不成还要重新写一套,还要基于新版本的接口重新写一套?当然不可能了,所以要用适配器模式。

     即,我们就用一个新版本的接口,有一个新版本接口的实现类,而且我们还要面向新版本的接口去开发,通过新版本的接口,同时还要使用老版本的这个实现类里面的功能逻辑。这时,我们就需要定义一个适配器类,如果我们要面向新接口NewInterface,去使用老版本接口的实现类oldObject,那么我们可用通过适配器

    NewInterface oldObject = new NewInterfaceAdapter(new OldInterfaceImpl());

     将老版本接口的实现类 作为参数,通过适配器 适配到新版本接口中,然后就可以通过 调用新版本接口中的方法,去调用老板本接口实现类中,与其同名方法的功能逻辑。从而达到 面向一套接口,执行一样的方法,却调用了不同实现类中的功能逻辑 的目的。代码如下

    public class AdapterPatternDemo {
    
        public static void main(String[] args) {
            NewInterface oldObject = new NewInterfaceAdapter(new OldInterfaceImpl());
            NewInterface newObject = new NewInterfaceImpl();
            oldObject.newExecute();
            newObject.newExecute();
        }
    
        /** 定义一个适配器类 */
        public static class NewInterfaceAdapter implements NewInterface{
    
            private OldInterface oldObject;
    
            public NewInterfaceAdapter (OldInterface oldObject){
                this.oldObject = oldObject;
            }
    
            @Override
            public void newExecute() {
                oldObject.oldExecute();
            }
        }
    
        /** 老版本接口 */
        public static interface OldInterface{
            void oldExecute();
        }
    
        /** 老版本接口的实现类 */
        public static class OldInterfaceImpl implements OldInterface {
            @Override
            public void oldExecute() {
                System.out.println("老版本接口实现的功能逻辑");
            }
        }
    
        /** 新版本接口 */
        public static interface NewInterface{
            void newExecute();
        }
    
        /** 新版本接口的实现类 */
        public static class NewInterfaceImpl implements NewInterface {
            @Override
            public void newExecute() {
                System.out.println("新版本接口实现的功能逻辑");
            }
        }
    
    }

    综上,所谓的适配器模式,简单的理解就是,

    你手上有新老两接口 和 一个老接口的实现类,但是 现在系统中,要强制面向新接口来开发,那么这个老接口的实现类,就不能直接用了,不能面向老接口来开发了。

    这时 就可以开发一个老接口到新接口的适配器,这个适配器是实现了新接口的,但是适配器中持有老接口实现类实例的引用,

    然后,适配器的新接口方法的实现,全部基于老接口实现类的老方法来实现即可。

    那么,对于调用方而言,只要使用适配器来开发,就可以通过面向新接口开发,但是底层使用老接口的实现类。

     在实际企业开发中的使用场景:

     1. 一般在一套系统不断升级的过程中,比如从v1.0升级到v2.0,v2.0升级到v3.0,有这种版本升级的时候,要把老接口适配成新接口,那么后面的代码,就都可以面向新接口来开发。

     2. 对于一些已有的第3方类库,比如redis客户端,或者是elasticsearch的客户端,它们都提供了一套自己的API,但是我们这里的需求是,需要面向我们这里的一套接口(比如通用的DAO数据访问接口)来进行编程。例如,我们的DAO接口,要求的接口风格都是:save、update、remove、list、get等,这些方法风格。然后,我们现在又搞了一个,比如说DAORedisImpl这么一个类,然后这个redis本身的这个客户端,提供的都是:get、set、、mset、mget等,这样的一套接口风格。那么,这时我们开发的DAORedisImpl,就是一个适配器,这个适配器实现的是我们的DAO接口,在我们的sava、update、remove等方法中,然后去调用redis客户端的get、set、mset、mget等方法。这样,就把redis客户端的这个接口,适配到了我们项目本身 通用的这个DAO接口上面来,那么 类似这个DAORedisImpl这样的类,它就是一个适配器。

    适配器模式,一般会随着项目开发过程中,随着功能的扩展,逐步引入很多的外部依赖,大量的使用,而不只是mysql,因为如果用mysql的话,它就是基于mybatis的那个mapper接口去做。

    3. 比如com.fazecast:jSerialComm:2.9.1项目中的jSerialComm-2.9.1.jar中,在com.fazecast.jSerialComm.SerialPort.java 里,通过内部类的形式,对OutputStream,jdk本身的抽象类进行了适配:代码如下:

        public final OutputStream getOutputStream() {
            this.outputStream = new SerialPort.SerialPortOutputStream();
            return this.outputStream;
        }
        private final class SerialPortOutputStream extends OutputStream {
            private byte[] byteBuffer = new byte[1];
    
            public SerialPortOutputStream() {
            }
    
            public final void write(int b) throws SerialPortIOException, SerialPortTimeoutException {
                if (SerialPort.this.portHandle == 0L) {
                    throw new SerialPortIOException("This port appears to have been shutdown or disconnected.");
                } else {
                    this.byteBuffer[0] = (byte)(b & 255);
                    int bytesWritten = SerialPort.this.writeBytes(SerialPort.this.portHandle, this.byteBuffer, 1L, 0L, SerialPort.this.timeoutMode);
                    if (bytesWritten < 0) {
                        throw new SerialPortIOException("No bytes written. This port appears to have been shutdown or disconnected.");
                    } else if (bytesWritten == 0) {
                        throw new SerialPortTimeoutException("The write operation timed out before all data was written.");
                    }
                }
            }
    
            public final void write(byte[] b) throws NullPointerException, SerialPortIOException, SerialPortTimeoutException {
                this.write(b, 0, b.length);
            }
    
            public final void write(byte[] b, int off, int len) throws NullPointerException, IndexOutOfBoundsException, SerialPortIOException, SerialPortTimeoutException {
                if (b == null) {
                    throw new NullPointerException("A null pointer was passed in for the write buffer.");
                } else if (len >= 0 && off >= 0 && off + len <= b.length) {
                    int numWritten;
                    for(int totalNumWritten = 0; totalNumWritten != len; totalNumWritten += numWritten) {
                        if (SerialPort.this.portHandle == 0L) {
                            throw new SerialPortIOException("This port appears to have been shutdown or disconnected.");
                        }
    
                        numWritten = SerialPort.this.writeBytes(SerialPort.this.portHandle, b, (long)(len - totalNumWritten), (long)(off + totalNumWritten), SerialPort.this.timeoutMode);
                        if (numWritten < 0) {
                            throw new SerialPortIOException("No bytes written. This port appears to have been shutdown or disconnected.");
                        }
    
                        if (numWritten == 0) {
                            throw new SerialPortTimeoutException("The write operation timed out before all data was written.");
                        }
                    }
    
                } else {
                    throw new IndexOutOfBoundsException("The specified write offset plus length extends past the end of the specified buffer.");
                }
            }
        }

    这里,SerialPortOutputStream.java 就是一个适配器,需要适配的实现直接在SerialPortOutputStream类中重写了OutputStream抽象类中的方法,比如write方法;

     然后,通过直接调用SerialPortOutputStream的无参构造器,获取SerialPortOutputStream适配器实例,就可以直接通过调用OutputStream这个抽象类的中的方法,去调用适SerialPortOutputStream适配器中实现的功能逻辑。与 AdapterPatternDemo 中的适配器 NewInterfaceAdapter 相比,NewInterfaceAdapter 调用了实现类,是通过适配器有参构造器的参数形式,传入到适配器中,在需要重写的适配器方法中间接调用的;而SerialPortOutputStream适配器,是直接重写在是适配器的方法之中。

    end

  • 相关阅读:
    对结对编程的测试
    用例
    结对编程 一
    个人项目总结与结对编程的开始
    7-6随便写写
    7-5个人日报
    7-4个人报告
    7.1-7.3个人日报
    6-30个人日报
    6-29个人日报
  • 原文地址:https://www.cnblogs.com/HarryVan/p/16264893.html
Copyright © 2020-2023  润新知