java/android 设计模式学习笔记(9)---代理模式

  这篇博客我们来介绍一下代理模式(Proxy Pattern),代理模式也成为委托模式,是一个非常重要的设计模式,不少设计模式也都会有代理模式的影子。代理在我们日常生活中也很常见,比如上网时连接的代理服务器地址,更比如我们平时租房子,将找房子的过程代理给中介等等,都是代理模式在日常生活中的使用例子。

  代理模式中的代理对象能够连接任何事物:一个网络连接,一个占用很多内存的大对象,一个文件,或者是一些复制起来代价很高甚至根本不可能复制的一些资源。总之,代理是一个由客户端调用去访问幕后真正服务的包装对象,使用代理可以很容易地转发到真正的对象上,或者在此基础上去提供额外的逻辑。代理模式中也可以提供额外的功能,比如在资源集中访问操作时提供缓存服务,或者在操作真正执行到对象之前进行前提条件的检查。对于客户端来说,使用代理对象,和使用真正的对象其实是类似的,因为他们都实现了同样的接口。

  转载请注明出处:http://blog.csdn.net/self_study/article/details/51628486

  PS:对技术感兴趣的同鞋加群544645972一起交流。

设计模式总目录

  java/android 设计模式学习笔记目录

特点

  代理模式为另一个对象提供一个代理以控制对这个对象的访问。使用代理模式创建代理,让代理对象控制对某个对象的访问,被代理的对象可以是远程的对象,创建开销很大的对象,或者是需要安全控制的对象,所以代理模式的使用场景为:当无法或者不想直接访问某个对象或访问某个对象存在困难时可以通过一个代理对象来间接访问,为了保证客户端的透明性,委托对象与代理对象需要实现同样的接口。

  代理模式可以大致分为静态代理和动态代理。静态代理模式的代码由程序员自己或通过一些自动化工具生成固定的代码再对其进行编译,也就是说我们的代码在运行前代理类的 class 编译文件就已经存在;而动态代理则与静态代理相反,在 Java 或者 Android 中通过反射机制动态地生成代理者的对象,也就是说我们在 code 阶段完全不需要知道代理谁,代理谁我们将会在执行阶段决定,在 Java 中,也提供了相关的动态代理接口 InvocationHandler 类,这个我们在后面源码的时候会用到。

  代理模式根据实际使用的场景也可以分为以下几种:

  • 远程代理(Remote Proxy):为某个在不同的内存地址空间的对象提供局部代理,使系统可以将 Server 部分的实现隐藏,以便 Client 可以不必考虑 Server 的存在,类似于 C/S 模式(主要拦截并控制远程方法的调用,做代理防火墙之类的);
  • 虚拟代理(Virtual Proxy):使用一个代理对象标识一个十分耗资源的对象,并在真正需要时才创建,实现一个延迟加载的机制;
  • 保护代理(Protection Proxy):是用代理控制对原始对象的访问,该类型的代理通常被用于原始对象有不同访问权限的情况;
  • 智能引用(Smart Proxy):在访问原始对象时执行一些自己的附加操作并对指向原始对象的引用计数;
  • 写时拷贝(克隆)代理(Copy-on-write Proxy):其实是虚拟代理的一个分支,提供了拷贝大对象的时候只有在对象真正变化后才会进行拷贝(克隆)的操作,即延迟拷贝。

需要注意的一点是,静态和动态代理都可以应用于上述的几种情形,两者是各自独立变化的。

UML类图

  我们来看看代理模式的 uml 类图:

  

据此我们可以写出代理模式的通用代码:

Subject.class

public abstract class Subject {
    public abstract void operation();
}

RealSubject.class

public class RealSubject extends Subject{
    @Override
    public void operation() {
        //the real operation
    }
}

ProxySubject.class

public class ProxySubject extends Subject{

    private Subject realSubject;

    public ProxySubject(Subject realSubject) {
        this.realSubject = realSubject;
    }

    @Override
    public void operation() {
        if (realSubject != null) {
            realSubject.operation();
        } else {
            //do something else
        }
    }
}

客户端 Client 代码

ProxySubject subject = new ProxySubject(new RealSubject());
subject.operation();

代理模式的角色:

  • Subject:抽象主题类
  • 该类的主要职责是声明真实主题与代理的共同接口方法,该类既可以是一个抽象类,也可以是一个接口;

  • RealSubjct:真实主题类
  • 该类也称为被委托类或被代理类,该类定义了代理所表示的真实对象,由其执行具体的业务逻辑方法,而客户端则通过代理类间接地调用真实主题类中定义的方法;

  • ProxySubject:代理类
  • 该类也称为委托类或代理类,该类持有一个对真实主题类的引用,在其所实现的接口方法中调用真实主题类中对应的接口方法,以此起到代理的作用;

  • Client:客户类,即使用代理类的部分。

Android 源码代理模式分析

  Android 源码里有不少关于代理模式的实现,最典型的比如源码中的 ActivityManagerProxy 代理类,其具体代理的是 ActivityManagerNative 的子类 ActivityManagerService 类,对 AMS 感兴趣的可以去网上查阅一下相关资料,这里就不详细介绍了。我们老规矩,来看看 uml 图:

  

我在之前的一篇博客:android 不能在子线程中更新ui的讨论和分析 中分析了 Activity 的创建过程,感兴趣的可以去看看,在博客中分析到 startActivity 方法调用到了 Instrumentation 的 execStartActivity 方法, execStartActivity 方法又会调用 ActivityManagerNative.getDefault().startActivity 方法,最后调用到了 ActivityManagerService 中,但是当时并没有很明白为什么会调用到 AMS 中,所以这里根据上面的 uml 图补充说明一下。其实不光是 Instrumentation 类会调用到 ActivityManagerNative.getDefault() 方法,ActivityManager 中的很多方法都调用到了 ActivityManagerNative.getDefault(),比如 moveTaskToFront 方法:

public void moveTaskToFront(int taskId, int flags, Bundle options) {
    try {
        ActivityManagerNative.getDefault().moveTaskToFront(taskId, flags, options);
    } catch (RemoteException e) {
        // System dead, we will be dead too soon!
    }
}

和 getRunningServices 方法等等等:

public List<RunningServiceInfo> getRunningServices(int maxNum)
        throws SecurityException {
    try {
        return ActivityManagerNative.getDefault()
                .getServices(maxNum, 0);
    } catch (RemoteException e) {
        // System dead, we will be dead too soon!
        return null;
    }
}

直接将函数调用转给了 ActivityManagerNative.getDefault() 方法,看看 getDefault() 函数:

public abstract class ActivityManagerNative extends Binder implements IActivityManager
{
    /**
     * Cast a Binder object into an activity manager interface, generating
     * a proxy if needed.
     */
    static public IActivityManager asInterface(IBinder obj) {
        if (obj == null) {
            return null;
        }
        IActivityManager in =
            (IActivityManager)obj.queryLocalInterface(descriptor);
        if (in != null) {
            return in;
        }

        return new ActivityManagerProxy(obj);
    }
...
    static public IActivityManager getDefault() {
        return gDefault.get();
    }
...
    private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
        protected IActivityManager create() {
            IBinder b = ServiceManager.getService("activity");
            if (false) {
                Log.v("ActivityManager", "default service binder = " + b);
            }
            IActivityManager am = asInterface(b);
            if (false) {
                Log.v("ActivityManager", "default service = " + am);
            }
            return am;
        }
    };
...
}

getDefault 函数通过一个 Singleton 对象对外提供, ServiceManager.getService(“activity”) 返回的是 ActivityManagerService 的 Binder 对象,我们在看看 asInterface 函数,如果 queryLocalInterface 方法返回的是 null ,那么就会去创建一个 ActivityManagerProxy 对象,并且将 AMS 这个 Binder 对象作为参数传递给 ActivityManagerProxy 对象,来简单看看 ActivityManagerProxy 的代码:

class ActivityManagerProxy implements IActivityManager
{
    public ActivityManagerProxy(IBinder remote)
    {
        mRemote = remote;
    }

    public IBinder asBinder()
    {
        return mRemote;
    }

    public int startActivity(IApplicationThread caller, String callingPackage, Intent intent,
            String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle options) throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken(IActivityManager.descriptor);
        ...
        mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
        reply.readException();
        ...
    }
...
}

看了这里的代码之后就一目了然了,代理对象的函数比如 startActivity 方法最终调用到的还是 AMS 中,ActivityManagerProxy 的作用只是作为代理而已。调用到 AMS 的后续步骤可以回到博客: android 不能在子线程中更新ui的讨论和分析 去看看,这里就重复了。

  还有几点需要在这里特殊说明一下:

  • 第一点:为什么 IActivityManager 需要实现 IInterface 接口,或者为什么 ActivityManagerProxy 需要继承 IInterface 接口的行为
  • 我们看看 IInterface 接口的定义:

/**
 * Base class for Binder interfaces.  When defining a new interface,
 * you must derive it from IInterface.
 */
public interface IInterface
{
    /**
     * Retrieve the Binder object associated with this interface.
     * You must use this instead of a plain cast, so that proxy objects
     * can return the correct result.
     */
    public IBinder asBinder();
}

有一个 asBinder 的方法,而 ActivityManagerProxy 的 asBinder 方法实现在上面已经贴出代码了,所以说 ActivityManagerProxy 中的 asBinder 函数是用来返回 AMS 这个 Binder 对象的;

  • 第二点,继承 IBinder 和 Binder 的作用
  • 我们知道 AMS 是由 SystemServer 来启动的,它是运行在另一个独立进程中的(这点和 WindowManagerService 类似,对 WMS 感兴趣的可以看看我的上篇博客: java/android 设计模式学习笔记(8)—桥接模式),而 ActivityManagerProxy 是运行于当前进程当中的,此时必须要用到跨进程的通信,继承自 IInterface ,IBinder 接口和 Binder 类也是必然的(关于 IPC 通信,感兴趣的可以去看看我的博客:android IPC通信(下)-AIDL)。到这里,如果对 AIDL 熟悉的童鞋,就应该发现了其实这个 ActivityManagerProxy 和 ActivityManagerService 类就相当于我们写了一个 aidl 文件之后,系统为我们自动生成文件中的 Proxy 类和 Stub 类,所以说 aidl 就是一个使用远程代理模式非常实际的例子。

    示例与源码

      看了上面的通用代码和 Android 源码的 AMS 机制之后,代理模式应该已经很清楚了,但是上面用到的都是静态代理,都是在编译阶段生成的 class 文件,所以这里以一个动态+保护代理模式的 demo 为例:

    动态+保护代理

      先看看它的 uml 类图:

      

    这里我们用到了 InvocationHandler 这个类,这是 Java 为我们提供的一个便捷动态代理接口,作用就是用来在运行时动态生成代理对象,在这里就不详细介绍了,感兴趣的可以去查阅相关资料。其次是保护代理,这里就以 ProxyA 和 ProxyB 两个代理类为例,一个只能调用 operationA 方法,另一个只能调用 operationB 方法。首先来看看 Subject 和 RealSubject 类:

    Subject.class

    public interface Subject {
    
        String operationA();
    
        String operationB();
    }

    RealSubjct.class

    public class RealSubject implements Subject{
        @Override
        public String operationA() {
            return "this is operationA";
        }
    
        @Override
        public String operationB() {
            return "this is operationB";
        }
    }

    定义完了抽象主题类和真实主题类之后,开始构造代理类,首先是代理类的虚基类:

    ISubjectProxy.class

    public abstract class ISubjectProxy implements InvocationHandler {
        protected Subject subject;
        public ISubjectProxy(Subject subject) {
            this.subject = subject;
        }
    }

    然后是 ProxyA 和 ProxyB 这两个代理子类:

    ProxyA.class

    public class ProxyA extends ISubjectProxy{
        public ProxyA(Subject subject) {
            super(subject);
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if (method.getName().equals("operationB")){
                throw new UnsupportedOperationException("ProxyA can‘t invoke operationB");
            }else if (method.getName().equals("operationA")) {
                return method.invoke(subject, args);
            }
            return null;
        }
    }

    ProxyB.class

    public class ProxyB extends ISubjectProxy{
        public ProxyB(Subject subject) {
            super(subject);
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if (method.getName().equals("operationA")){
                throw new UnsupportedOperationException("ProxyB can‘t invoke operationA");
            } else if (method.getName().equals("operationB")) {
                return method.invoke(subject, args);
            }
            return null;
        }
    }

    最后是 Client 的代码:

    Subject subject = new RealSubject();
    ISubjectProxy proxy = null;
    switch (v.getId()) {
        case R.id.proxy_a:
            proxy = new ProxyA(subject);
            break;
        case R.id.proxy_b:
            proxy = new ProxyB(subject);
            break;
    }
    Subject sub = (Subject) Proxy.newProxyInstance(subject.getClass().getClassLoader(),
            subject.getClass().getInterfaces(), proxy);
    try {
        Log.e("Shawn", sub.operationA());
    }catch (UnsupportedOperationException e){
        Log.e("Shawn", e.getMessage());
    }
    try {
        Log.e("Shawn", sub.operationB());
    }catch (UnsupportedOperationException e){
        Log.e("Shawn", e.getMessage());
    }

    结果:

    com.android.proxypattern E/Shawn: this is operationA
    com.android.proxypattern E/Shawn: ProxyA can‘t invoke operationB
    com.android.proxypattern E/Shawn: ProxyB can‘t invoke operationA
    com.android.proxypattern E/Shawn: this is operationB

    代码很简单,第一点是使用 InvocationHandler 实现了动态代理,第二点是根据 method 的名字实现了保护代理。

      这里只总结了保护代理,虚拟代理这里简单举个例子: moduleA 为 modleB 的 lib,所以如果在 moduleA 中需要用到 moduleB 实现的对象,就可以使用代理模式,具体步骤是在 moduleA 中定义接口,moduleB 去实现该接口,moduleA 中定义一个代理对象并提供一些默认操作,当 moduleB 的相关模块初始化之后,将该对象设置到 moduleA 的代理对象中以替代原来的默认实现,之后代理对象就能成功调用 moduleB 中定义的行为了,而且也实现了延迟加载。远程代理和其他的代理模式大家去网上查阅一下相关资料,万变不离其宗,道理是一样的。

    总结

      代理模式应用广泛,超级广泛,很多设计模式都会有代理模式的影子,有些模式单独作为一种设计模式,倒不如说是对代理模式的一种针对性优化,而且代理模式的缺点也很少,总结一下代理模式的优缺点:

    • 优点:
      1. 代理作为调用着和真实对象的中间层,降低了模块间和系统的耦合性;
      2. 可以以一个小对象代理一个大对象,达到优化系统提高运行速度的目的;
      3. 提供 RealSubject 的权限管理;
      4. 容易扩展,RealSubject 和 ProxySubject 都接口化了,RealSubject更改业务后只要接口不变,ProxySubject 可以不做任何修改。
    • 缺点:
      1. 调用者和真实对象多了一个中间层,对象的创建,函数调用使用的反射等都增加调用响应的时间;
      2. 设计模式的通病:类的增加,不过这点也不严重。

    适配器 VS 装饰者 VS 桥接 VS 代理

      这四个都是结构型设计模式,他们有些类似,在实际使用过程中也容易搞混,我们在这就给他们做一个对比:

    适配器模式

      适配器模式和其他三个设计模式一般不容易搞混,它的作用是将原来不兼容的两个类融合在一起,uml 图也和其他的差别很大。

      uml 类图:

      

    装饰者模式

      装饰者模式结构上类似于代理模式,但是和代理模式的目的是不一样的,装饰者是用来动态地给一个对象添加一些额外的职责,装饰者模式为对象加上行为,而代理则是控制访问。

      uml 类图:

      

    桥接模式

      桥接模式的目的是为了将抽象部分与实现部分分离,使他们都可以独立地进行变化,所以说他们两个部分是独立的,没有实现自同一个接口,这是桥接模式与代理模式,装饰者模式的区别。

      uml 类图:

      

    代理模式

      代理模式为另一个对象提供代表,以便控制客户对对象的访问,管理的方式有很多种,比如远程代理和虚拟代理等,这个在上面有,这里就不说了,而装饰者模式则是为了扩展对象。

      uml 类图:

      

    源码下载

      https://github.com/zhaozepeng/Design-Patterns/tree/master/ProxyPattern

    引用

    http://blog.csdn.net/jason0539/article/details/22974405

    https://en.wikipedia.org/wiki/Proxy_pattern

    http://blog.csdn.net/l2show/article/details/46992495

    时间: 2024-08-02 02:48:48

    java/android 设计模式学习笔记(9)---代理模式的相关文章

    java/android 设计模式学习笔记(14)---外观模式

    这篇博客来介绍外观模式(Facade Pattern),外观模式也称为门面模式,它在开发过程中运用频率非常高,尤其是第三方 SDK 基本很大概率都会使用外观模式.通过一个外观类使得整个子系统只有一个统一的高层的接口,这样能够降低用户的使用成本,也对用户屏蔽了很多实现细节.当然,在我们的开发过程中,外观模式也是我们封装 API 的常用手段,例如网络模块.ImageLoader 模块等.其实我们在开发过程中可能已经使用过很多次外观模式,只是没有从理论层面去了解它. 转载请注明出处:http://bl

    java/android 设计模式学习笔记(10)---建造者模式

    这篇博客我们来介绍一下建造者模式(Builder Pattern),建造者模式又被称为生成器模式,是创造性模式之一,与工厂方法模式和抽象工厂模式不同,后两者的目的是为了实现多态性,而 Builder 模式的目的则是为了将对象的构建与展示分离.Builder 模式是一步一步创建一个复杂对象的创建型模式,它允许用户在不知道内部构建细节的情况下,可以更精细地控制对象的构造流程.一个复杂的对象有大量的组成部分,比如汽车它有车轮.方向盘.发动机.以及各种各样的小零件,要将这些部件装配成一辆汽车,这个装配过

    java/android 设计模式学习笔记(7)---装饰者模式

    这篇将会介绍装饰者模式(Decorator Pattern),装饰者模式也称为包装模式(Wrapper Pattern),结构型模式之一,其使用一种对客户端透明的方式来动态的扩展对象的功能,同时它也是继承关系的一种替代方案之一,但比继承更加灵活.在现实生活中也可以看到很多装饰者模式的例子,或者可以大胆的说装饰者模式无处不在,就拿一件东西来说,可以给它披上无数层不一样的外壳,但是这件东西还是这件东西,外壳不过是用来扩展这个东西的功能而已,这就是装饰者模式,装饰者的这个角色也许各不相同但是被装饰的对

    java/android 设计模式学习笔记(13)---享元模式

    这篇我们来介绍一下享元模式(Flyweight Pattern),Flyweight 代表轻量级的意思,享元模式是对象池的一种实现.享元模式用来尽可能减少内存使用量,它适合用于可能存在大量重复对象的场景,缓存可共享的对象,来达到对象共享和避免创建过多对象的效果,这样一来就可以提升性能,避免内存移除和频繁 GC 等. 享元模式的一个经典使用案例是文本系统中图形显示所用的数据结构,一个文本系统能够显示的字符种类就是那么几十上百个,那么就定义这么些基础字符对象,存储每个字符的显示外形和其他的格式化数据

    java/android 设计模式学习笔记(12)---组合模式

    这篇我们来介绍一下组合模式(Composite Pattern),它也称为部分整体模式(Part-Whole Pattern),结构型模式之一.组合模式比较简单,它将一组相似的对象看作一个对象处理,并根据一个树状结构来组合对象,然后提供一个统一的方法去访问相应的对象,以此忽略掉对象与对象集合之间的差别.这个最典型的例子就是数据结构中的树了,如果一个节点有子节点,那么它就是枝干节点,如果没有子节点,那么它就是叶子节点,那么怎么把枝干节点和叶子节点统一当作一种对象处理呢?这就需要用到组合模式了. 转

    java/android 设计模式学习笔记(16)---命令模式

    这篇博客我们来介绍一下命令模式(Command Pattern),它是行为型设计模式之一.命令模式相对于其他的设计模式更为灵活多变,我们接触比较多的命令模式个例无非就是程序菜单命令,如在操作系统中,我们点击关机命令,系统就会执行一系列的操作,如先是暂停处理事件,保存系统的一些配置,然后结束程序进程,最后调用内核命令关闭计算机等,对于这一系列的命令,用户不用去管,用户只需点击系统的关机按钮即可完成如上一系列的命令.而我们的命令模式其实也与之相同,将一系列的方法调用封装,用户只需调用一个方法执行,那

    java/android 设计模式学习笔记(3)---工厂方法模式

    这篇来介绍一下工厂方法模式(Factory Method Pattern),在实际开发过程中我们都习惯于直接使用 new 关键字用来创建一个对象,可是有时候对象的创造需要一系列的步骤:你可能需要计算或取得对象的初始设置:选择生成哪个子对象实例:或在生成你需要的对象之前必须先生成一些辅助功能的对象,这个时候就需要了解该对象创建的细节,也就是说使用的地方与该对象的实现耦合在了一起,不利于扩展,为了解决这个问题就需要用到我们的工厂方法模式,它适合那些创建复杂的对象的场景,工厂方法模式也是一个使用频率很

    java/android 设计模式学习笔记(5)---对象池模式

    这次要介绍一下对象池模式(Object Pool Pattern),这个模式为常见 23 种设计模式之外的设计模式,介绍的初衷主要是在平时的 android 开发中经常会看到,比如 ThreadPool 和 MessagePool 等. 在 java 中,所有对象的内存由虚拟机管理,所以在某些情况下,需要频繁创建一些生命周期很短使用完之后就可以立即销毁,但是数量很大的对象集合,那么此时 GC 的次数必然会增加,这时候为了减小系统 GC 的压力,对象池模式就很适用了.对象池模式也是创建型模式之一,

    java/android 设计模式学习笔记(4)---抽象工厂模式

    再来介绍一下抽象工厂模式(Abstact Factory Pattern),也是创建型模式之一,上篇博客主要介绍了工厂方法模式.抽象工厂模式和工厂方法模式稍有区别.工厂方法模式中工厂类生产出来的产品都是具体的,也就是说每个工厂都会生产某一种具体的产品,但是如果工厂类中所生产出来的产品是多种多样的,工厂方法模式也就不再适用了,就要使用抽象工厂模式了. 抽象工厂模式的起源或者最早的应用,是对不同操作系统的图形化解决方案,比如在不同操作系统中的按钮和文字框的不同处理,展示效果也不一样,对于每一个操作系