软件设计模式学习(十六)代理模式


当直接访问某些对象存在问题时,能够经过一个代理对象来间接访问,为了保证客户端使用的透明性,所访问的真实对象与代理对象须要实现相同的接口。java


模式动机

某些状况下,一个客户不想或不能直接引用一个对象,此时能够经过一个称之为代理的第三者实现间接引用。代理对象在客户端和目标对象之间起到中介做用,而且能够经过代理对象去掉客户不能看到的内容和添加客户须要的额外服务。程序员


模式定义

给某一个对象提供一个代理,并由代理对象控制对原对象的引用。代理模式的英文叫作 Proxy 或 Surrogate,它是一种对象结构模式。编程


模式结构

在这里插入图片描述

  1. Subject(抽象主题角色)数组

    声明了真实主题和代理主题的公共接口,这样一来在任何使用真实主题的地方均可以使用代理主题。客户端针对抽象主题角色编程。ide

  2. Proxy(代理主题角色)工具

    代理主题角色内部包含对真实主题的引用,从而能够在任什么时候候操做真实主题角色。学习

    代理主题角色中提供一个与真实主题角色相同的接口,以便在任什么时候候替代真实主体。测试

    代理主题角色还能够控制对真实主题的使用,负责在须要时建立和删除真实主题对象,并对真实主题对象的使用加以约束。优化

    代理角色一般在客户端调用所引用的真实主题操做以前或以后执行其余操做,而不只仅只是单纯调用真实主题对象中的操做。this

  3. RealSubject(真实主题角色)

    真实主题角色定义了代理角色所表明的真实对象,真实主题角色中实现真实的业务,客户端经过代理主题角色间接调用真实主题角色中定义的方法。


模式实例与解析

在一个论坛已注册用户和游客权限不一样,已注册用户拥有发帖、修改注册信息、修改本身帖子等功能;而游客只能看到别人发的贴子,没有其余权限。本实例中咱们使用代理模式中的保护代理,该代理用于控制对一个对象的访问,能够给不一样用户提供不一样级别的使用权限。

在这里插入图片描述

  1. 抽象主题角色 AbstractPermission(抽象权限类)

    AbstractPermission 做为抽象权限类,充当抽象主题角色,在其中声明了真实主题角色所提供的业务方法,它是真实主题角色和代理主题角色的公共接口

    public interface AbstractPermission {
    
        public void modifyUserInfo();
    
        public void viewNote();
    
        public void publishNote();
    
        public void modifyNote();
    
        public void setLevel(int level);
    }
  2. 真实主题角色 RealPermission(真实权限类)

    RealPermission 是真实主题角色,它实现了在抽象主题角色中定义的方法,因为种种缘由客户端没法访问其中内容。

    public class RealPermission implements AbstractPermission {
    
        @Override
        public void modifyUserInfo() {
            System.out.println("修改用户信息");
        }
    
        @Override
        public void viewNote() { }
    
        @Override
        public void publishNote() {
            System.out.println("发布新帖");
        }
    
        @Override
        public void modifyNote() {
            System.out.println("修改发帖内容");
        }
    
        @Override
        public void setLevel(int level) { }
    }
  3. 代理主题角色 PermissionProxy(权限代理类)

    PermissionProxy 是代理主题角色,它也实现了抽象主题角色接口,同时在 PermissionProxy 中定义了一个 RealPermission 对象,用于调用 RealPermission 中定义的真实业务方法。经过引入 PermissionProxy 类来对系统的使用权限进行控制,这就是保护代理的用途。

    public class PermissionProxy implements AbstractPermission {
    
        private RealPermission permission = new RealPermission();
        private int level = 0;
    
        @Override
        public void modifyUserInfo() {
            if (0 == level) {
                System.out.println("对不起,你没有该权限");
            } else if (1 == level) {
                permission.modifyUserInfo();
            }
        }
    
        @Override
        public void viewNote() {
            System.out.println("查看帖子");
        }
    
        @Override
        public void publishNote() {
            if (0 == level) {
                System.out.println("对不起,你没有该权限");
            } else if (1 == level) {
                permission.publishNote();
            }
        }
    
        @Override
        public void modifyNote() {
            if (0 == level) {
                System.out.println("对不起,你没有该权限");
            } else if (1 == level) {
                permission.modifyNote();
            }
        }
    
        @Override
        public void setLevel(int level) {
            this.level = level;
        }
    }
  4. 客户端测试类 Client

    public class Client {
    
        public static void main(String[] args) {
    
            AbstractPermission permission = new PermissionProxy();
    
            permission.modifyUserInfo();
            permission.viewNote();
            permission.publishNote();
            permission.modifyNote();
    
            System.out.println("-------------------------");
    
            permission.setLevel(1);
            permission.modifyUserInfo();
            permission.viewNote();
            permission.publishNote();
            permission.modifyNote();
        }
    }
  5. 运行结果
    在这里插入图片描述

    代理类能够对用户访问权限进行控制,所以有些用户无权调用真实业务类的某些方法,当用户权限改变时,则能够访问这些方法。若是须要增长并使用新的代理类,首先将新增代理类做为抽象主题角色的子类,实如今抽象主题中声明的方法。


模式优缺点

代理模式优势以下:

  1. 代理模式能协调调用者和被调用者,在必定程度上下降了系统的耦合度
  2. 远程代理使客户端能访问在远程机器上的对象
  3. 虚拟代理经过使用一个小对象来表明一个大对象,能够减小系统资源的消耗,对系统进行优化并提升速度
  4. 保护代理能够控制对真实对象的使用权限

代理模式缺点以下:

  1. 因为在客户端和真实主题之间增长了代理对象,所以可能会形成请求的处理速度变慢。
  2. 有些代理模式的实现十分复杂。

模式使用环境

根据代理模式的使用目的,常见的代理模式有如下几个类型。

  1. 远程(Remote)代理:为一个位于不一样的地址空间的对象提供一个本地的代理对象,这个不一样的地址空间能够在同一台主机,也能够是另外一台主机。
  2. 虚拟(Virtual)代理:若是须要建立一个资源消耗较大的对象,能够先建立一个消耗较小的对象来表示,真实对象只在须要时才会被真正建立。
  3. Copy-on-Write代理:它是虚拟代理的一种,把复杂操做延迟到只在客户端真正须要时才执行。
  4. 保护(Protect or Access)代理:控制对一个对象的访问,能够给不一样的用户提供不一样级别的使用权限。
  5. 缓冲(Cache)代理:为某一个目标操做的结果提供临时的存储空间,以便多个客户能够共享这些结果。
  6. 防火墙(FireWall)代理:保护目标不让恶意用户接近
  7. 同步化(Synchronization)代理:使几个用户能同时使用一个对象而没有冲突
  8. 智能(Smart Reference)引用:当一个对象被引用时,提供一些额外的操做,如将此对象被调用的次数记录下来

静态代理

所谓静态代理,就是由程序员建立或特定工具自动生成源代码,也就是在编译时就已经将接口,被代理类,代理类等肯定下来。在程序运行以前,代理类的 .class 文件就已经生成。简单来讲,上述的实例就属于静态代理,PermissionProxy 代理类是咱们定义好的,在程序运行以前就已经编译完成。


动态代理

传统的代理模式中,客户端经过 ProxySubject 调用 RealSubject 类的方法,同时还在代理类中封装了其余方法,能够处理一些其余问题。若是按照这种方法使用代理模式,那么真实主题角色必须是是事先已经存在的,并将其做为代理对象的内部成员属性。若是一个真实主题角色必须对应一个代理主题角色,这将致使系统中的类的个数急剧增长,所以须要想办法减小系统中类的个数。

Java 自带的基于接口的动态代理(即只能实现接口的代理)能在运行时根据咱们在 Java 代码中的指示动态生成的代理类,其实现相关类位于 java.lang.reflect 包,运行时动态地对某些东西做代理,主要涉及两个类

  1. InvocationHandler 接口

    InvocationHandler is the interface implemented by the invocation handler of a proxy instance.
    Each proxy instance has an associated invocation handler.When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler.

    InvocationHandler 是由代理实例的调用处理程序实现的接口。每一个代理实例都有一个关联的调用处理程序。InvocationHandler 接口中定义了 invoke 方法,当代理实例调用某个方法时,该方法的调用将被编码并调度到其调用处理程序的 invoke 方法处理。

    /**
     *	处理代理实例上的方法调用并返回结果
     *	proxy 表示动态代理类
     *	method 表示须要代理的方法
     *	args 表示代理方法的参数数组
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
  2. Proxy 类

    /**
     *	该类提供了用于为接口建立代理实例的静态方法
     */
    public class Proxy implements java.io.Serializable {
        ...
         /**
          *	根据传入的接口类型返回一个动态建立的代理类实例
          *	loader 表示被代理类的类加载器
          * interfaces 表示被代理类实现的接口列表(与真实主题类的接口列表一致)
          *	h 表示所指派的调用处理程序类
          */
    	public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) {
            ...
    }

    下面经过一个简单实例来学习动态代理,如今有两个真实主题类分别是 RealSubjectA 和 RealSubjectB,它们对于抽象主题类中定义的抽象方法 request() 提供了不一样的实现,在不增长新的代理类的状况下,使得客户端经过一个代理类来动态选择所代理的真实主题对象

    1. 抽象主题接口 AbstractSubject

      public interface AbstractSubject {
      
          public void request();
      
      }
    2. 真实主题类一 RealSubjectA

      public class RealSubjectA implements AbstractSubject {
      
          @Override
          public void request() {
              System.out.println("真实主题类A");
          }
      }
    3. 真实主题类二 RealSubjectB

      public class RealSubjectB implements AbstractSubject {
      
          @Override
          public void request() {
              System.out.println("真实主题类B");
          }
      }
    4. 动态代理类 DynamicProxy

      public class DynamicProxy implements InvocationHandler {
      
          private Object obj;
      
          public DynamicProxy() {}
      
          public DynamicProxy(Object obj) {
              this.obj = obj;
          }
      
          //  实现 invoke() 方法,调用在真实主题类中定义的方法
          @Override
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      
              System.out.println("调用以前");
              //  利用反射调用方法,若是方法没有返回值则为 null
              Object result = method.invoke(obj, args);
              System.out.println("调用以后");
              return result;
          }
      }
    5. 客户端测试类 Client

      public class Client {
      
          public static void main(String[] args) {
      
              AbstractSubject subject = new RealSubjectA();
              InvocationHandler handler = new DynamicProxy(subject);
              AbstractSubject subjectProxy = (AbstractSubject) Proxy.newProxyInstance(subject.getClass().getClassLoader(),
                      subject.getClass().getInterfaces(), handler);
              subjectProxy.request();
      
              System.out.println("-----------------------------");
      
              subject = new RealSubjectB();
              handler = new DynamicProxy(subject);
              subjectProxy = (AbstractSubject) Proxy.newProxyInstance(subject.getClass().getClassLoader(),
                      subject.getClass().getInterfaces(), handler);
              subjectProxy.request();
          }
      }
    6. 运行结果
      在这里插入图片描述

相关文章
相关标签/搜索