结合代码分析装饰者模式和代理模式的区别

前言  

  最近在看动态代理的时候突然想到这个问题。代理模式和装饰者模式都是为已有方法添加新功能或者新处理的,那么他们之间有什么区别呢?能力有限,所知所写也仅限于一家之言,之后再有其他思考一定回来补充!!

正文

  表面上看都是添加新功能,好像很像,但是我认为这二者之间的区别还是比较明显的。先说结论:区别就在于装饰者模式用于添加新方法;而代理模式则用于增强原方法。为什么这么说呢,看完下面的具体解释在回头来看,可能会更清楚一些。

  首先装饰者模式我们很常用的就是流处理了,各种节点流,处理流。最基本的InputStream无法读取一整行,所以需要包一层BufferedReader。我们通过

BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("XXX")));

  构造BufferedReader,并调用它的readLine方法,所以可以说我们很清楚自己需要什么,并因此调用了相应的代码。
但是代理模式则不一样,你可能并不知道你调用的方法背着你偷偷摸摸地做了什么。举个栗子来说,Android中实现跨进程通信的方法其中之一是AIDL。那么AIDL在实现的时候是怎么做的呢?(由于本文是侧重设计模式的,关于AIDL的知识请看这里。另外由于比较懒,我是用这个链接中的代码进行讲解的。。。)
背景是我们有一个接口是这样的:

interface IDownloadService {
 /**
  * 下载
  * @param url
  */
  void download(String url);
}

  然后我们肯定有一个变量是IDownloadService类型的,我们姑且叫他mDownloadService。并且我们调用写好的方法时是这么做的:mDownloadService.download("XXX");
那么我们是怎么获取这个mDownloadService变量的呢?是通过发出绑定请求,然后在回调方法中

public void onServiceConnected(ComponentName className, IBinder service) {
    mDownloadService = IDownloadService.Stub.asInterface(service);
}

  OK,到这里其实就涉及到了代理模式了,关键就在于这个asInterface和download方法。那么接下来再解释一下是怎么用到代理模式的。

  下面是AIDL代码生成器帮我们为IDownloadService生成的代码,除了声明了download方法外,还添加了我们刚才看见的Stub类以及Stub的代理类Proxy(代理类喔,有没有很兴奋)

public interface IDownloadService extends android.os.IInterface {
    /**
     * 下载
     *
     * @param url
     */
    public void download(java.lang.String url) throws android.os.RemoteException;

    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements com.cundong.touch.IDownloadService {

        static final int TRANSACTION_download = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);  

        private static final java.lang.String DESCRIPTOR = "com.cundong.touch.IDownloadService";

        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an com.cundong.touch.IDownloadService interface,
         * generating a proxy if needed.
         * 
         * 注:
         * 这个方法里面有个处理,通过queryLocalInterface查询,如果服务端和客户端都是在同一个进程,那么就不需要跨进程了,直接将IDownloadService当做普通的对象来使用即可,否则返回远程对象的代理
         */
        public static com.cundong.touch.IDownloadService asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.cundong.touch.IDownloadService))) {
                return ((com.cundong.touch.IDownloadService) iin);
            }
            return new com.cundong.touch.IDownloadService.Stub.Proxy(obj);
        }

        /**
        * 返回Binder实例
        */
        @Override
        public android.os.IBinder asBinder() {
            return this;
        }

        /**
        * 根据code参数来处理,这里面会调用真正的业务实现类
        */
        @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_download: {
                    data.enforceInterface(DESCRIPTOR);
                    java.lang.String _arg0;
                    _arg0 = data.readString();
                    this.download(_arg0);
                    reply.writeNoException();
                    return true;
                }               
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.cundong.touch.IDownloadService {
            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }

            //Proxy的asBinder()返回位于本地接口的远程代理
            @Override
            public android.os.IBinder asBinder() {
                return mRemote;
            }

            public java.lang.String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }

            /**
             * 下载
             *
             * @param url
             */
            @Override
            public void download(java.lang.String url) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeString(url);

                    //Proxy中, 业务接口中其实没有做任何业务逻辑处理,仅仅是收集本地参数,序列化后通过IBinder的transact方法,发给服务器端,并且通过_reply来接收服务器端的处理结果
                    mRemote.transact(Stub.TRANSACTION_download, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }           
        }
    }
}

  看到这里,可能会有些混乱,我来解释一下。我们知道通过IDownloadService.Stub.asInterface(service);来获取mDownloadService。而asInterface方法会根据当前是否是跨进程操作来返回不同的Stub对象:如果不是跨进程则直接返回一个普通的IDownloadService对象。但是如果是跨进程的话,我们可以看见其实是构建了一个Proxy对象来返回,并且在Proxy中重写了download方法,而之所以重写,是因为跨进程通信需要将参数序列化。接下来在重写的download方法中会通过mRemote.transact调用远端的onTransact方法,而在onTransact中会先将参数反序列化,然后才会调用真正的download业务逻辑。也就是说这个代理类最终干的事情就是在原有方法前添加了序列化和反序列化的处理
  所以也可以说,装饰者模式类似于工具,由你来进行组合使用。而代理模式则是对原有方法的扩充,表面上看还是调用一样的方法,然而内部实现可能早已不同了。

你可能感兴趣的:(Android)