代理模式


代理模式


  • 代理模式也被称为委托模式,为其他对象提供一种代理以控制对这个对象的访问(Provide a surrogate or placeholder for another object to control access to it)。

  • 代理模式的通用类图中包含三种角色,抽象主体角色(Subject)、具体主题角色(RealSubject)、代理主题角色(Proxy),其通用类图如下:

    1. Subject抽象主体角色:可以使抽象类也可以是接口,是一个普通的业务类型定义。
    2. RealSubject具体主题角色:也叫做被委托角色、被代理角色,是业务逻辑的具体执行者。
    3. Proxy代理主题角色:也叫做委托类、代理类,它负责对真实角色的应用,把所有抽象主题类定义的方法限制委托给真实主题角色实现,并且在真实主题角色处理完成前后做预处理和后处理工作
    • 代理模式通用类图的代码如下:
    • public interface Subject{
          public void request();
      }
      
      public class RealSubject implements Subject{
      
          @Override
          public void request() {
              // TODO Auto-generated method stub
              //具体的业务逻辑处理
          }    
      }
      
      public class Proxy implements Subject{
          private Subject subject = null;
          public Proxy(Subject subject){
              this.subject = subject;
          }
          @Override
          public void request() {
              // TODO Auto-generated method stub
              this.before();
              subject.request();
              this.after();
          }
          private void after() {
              //预处理
          }
          private void before() {
              //后处理
          }
      }
  • 代理模式的优点

    1. 职责清晰真实角色就是实现实际的业务逻辑,不用关心其他非本职工作的事务,通过后期代理完成非本职工作。
    2. 高扩展性具体主题角色可以随时发生变化,只要它实现了接口,而不用关心它如何变化,只要接口没变,代理类可以在完全不做修改的情况下使用。
    3. 智能化通过动态代理的方式实现在编码阶段不需要知道代理的对象。
  • 代理模式的具体应用

    • 普通代理模式,在该模式下调用者只知道代理而不用知道真实角色,屏蔽了真实角色的变更对高层模块的影响,在实际项目中,一般都是通过约定来禁止new一个真实的角色。
    • 普通代理模式的实例类图如下:

    • 具体源码如下,通过GamePlayerProxy的构造函数中创建被代理者对象。
    • public interface IGamePlayer {
          public void login(String userName, String passwd);
          public void killBoss();
          public void upgrade();
      }
      
      public class GamePlayer implements IGamePlayer{
          public GamePlayer(IGamePlayer _proxy) throws Exception{
              if(_proxy == null)  //检查代理类是否能创建真是角色对象
                  throw new Exception("该代理不成代理该类");
          }
          @Override
          public void login(String userName, String passwd) {
              // TODO Auto-generated method stub
              System.out.println(userName + "登入游戏");
          }
          @Override
          public void killBoss() {
              // TODO Auto-generated method stub
              System.out.print("在打怪");
          }
      
          @Override
          public void upgrade() {
              // TODO Auto-generated method stub
              System.out.println("升级");
          }
      }
      
      public class GamePlayerProxy implements IGamePlayer{
          private IGamePlayer gameplayer = null;
          public GamePlayerProxy(){
              try {
                  this.gameplayer = new GamePlayer(this);
              } catch (Exception e) {
                  // TODO Auto-generated catch block
                  e.printStackTrace();
              }
          }
          @Override
          public void login(String userName, String passwd) {
              // TODO Auto-generated method stub
              this.gameplayer.login(userName, passwd);
          }
      
          @Override
          public void killBoss() {
              // TODO Auto-generated method stub
              this.gameplayer.killBoss();
          }
      
          @Override
          public void upgrade() {
              // TODO Auto-generated method stub
              this.gameplayer.upgrade();
          }
      
      }
      
      public class Client{
          public static void main(String[] args){
              IGamePlayer proxy = new GamePlayerProxy();
              proxy.login("Danny", "passwd");
              proxy.killBoss();
              proxy.upgrade();
          }
      }
    • 强制代理模式,该模式是通过真实角色查找到代理模式,其通用类图比普通代理类图只在接口处增加了getProxy()方法,其实例类图如下:

    • 具体源码如下,其中通过GamePlayer的getProxy()方法取得代理,并且在GamePlayer私有方法isProxy()中增加了判断是否是通过代理访问方法
    • public interface IGamePlayer {
          public void login(String userName, String passwd);
          public void killBoss();
          public void upgrade();
          public IGamePlayer getProxy();
      }
      
      public class GamePlayer implements IGamePlayer{
          private IGamePlayer proxy = null;
          @Override
          public void login(String userName, String passwd) {
              // TODO Auto-generated method stub
              if(this.isProxy())
                  System.out.println(userName + "登入游戏");
              else
                  System.out.println("请通过代理访问游戏");
          }
          @Override
          public void killBoss() {
              // TODO Auto-generated method stub
              if(this.isProxy())
                  System.out.print("在打怪");
              else
                  System.out.println("请通过代理访问游戏");
          }
      
          @Override
          public void upgrade() {
              // TODO Auto-generated method stub
              if(this.isProxy())
                  System.out.println("升级");
              else
                  System.out.println("请通过代理访问游戏");
          }
          @Override
          public IGamePlayer getProxy() {
              // TODO Auto-generated method stub
              if(this.proxy == null)
                  this.proxy = new GamePlayerProxy(this);
              return this.proxy;
          }
          private boolean isProxy(){
              StackTraceElement[] stack = Thread.currentThread().getStackTrace();
              for(int i = 0; i < stack.length; i++){
                  String name = stack[i].getClassName();
                  if(name.contains("Proxy") && this.proxy != null)
                      return true;
              }
              return false;
          }
      }
      
      public class GamePlayerProxy implements IGamePlayer{
          private IGamePlayer gameplayer = null;
          protected GamePlayerProxy(IGamePlayer player){
              this.gameplayer = player;
          }
          @Override
          public void login(String userName, String passwd) {
              // TODO Auto-generated method stub
              this.gameplayer.login(userName, passwd);
          }
      
          @Override
          public void killBoss() {
              // TODO Auto-generated method stub
              this.gameplayer.killBoss();
          }
      
          @Override
          public void upgrade() {
              // TODO Auto-generated method stub
              this.gameplayer.upgrade();
          }
          @Override
          public IGamePlayer getProxy() {
              // TODO Auto-generated method stub
              return this;
          }
      }
      
      public class Client {
      
          /**
           * @param args
           */
          public static void main(String[] args) {
              // TODO Auto-generated method stub
              IGamePlayer player = new GamePlayer();
              IGamePlayer proxy = player.getProxy();
              proxy.login("Danny", "passwd");
              proxy.killBoss();
              proxy.upgrade();
          }
      }
    • 代理类可以为真实角色预处理消息、过滤消息、转发消息、事后处理等功能。代理类不仅仅可以有自己的运算方法,通常情况下代理的职责并不单一,它可以组合其它的真实角色,也可是实现自己的职责,如计费等。如下是代理实现计费的通用类图:

    • 动态代理在实现阶段不用关心代理谁,而在运行阶段才指定代理哪个对象,现在的面向切面编程(Aspect Oriented Programming)就是采用了动态代理的机制。在该类图中增加了一个InvocationHandler接口和GamePlayer类,作用就是产生一个对象的代理对象,其中InvocationHandler是JDK提供的动态代理接口,对被代理的方法进行代理,其具体实例类图如下:

    • 实例类图的源码如下:
    • public class GamePlayerIH implements InvocationHandler{
          //被代理的实例
          private Object obj = null;
          public GamePlayerIH(Object obj){
              this.obj = obj;
          }
          //返回一个代理对象
          public Object bind(){
              return Proxy.newProxyInstance(this.obj.getClass().getClassLoader(), this.obj.getClass().getInterfaces(), this);
          }
          @Override
          public Object invoke(Object proxy, Method method, Object[] args)    //调用被代理的方法
                  throws Throwable {
              // TODO Auto-generated method stub
              Object result = method.invoke(this.obj, args);
      if(method.getName().equalsIgnoreCase("login")){
      System.out.println("代练登入游戏");
      }
      return result; } } public class Client{ /** * @param args */ public static void main(String[] args) { IGamePlayer player = new GamePlayer(); IGamePlayer proxy = (IGamePlayer)new GamePlayerIH(player).bind(); proxy.login("zhangsan", "passwd"); proxy.killBoss(); proxy.upgrade(); } }
    •  动态代理的通用类图如下:

很简单的两条独立发展的路线,动态代理实现代理的职责,具体的业务逻辑RealSubject实现相关的逻辑功能,两者之间没有必然的耦合。通知从另一个切面切入,最终在高层模块也就是Client进行耦合,完成逻辑的封装任务。

    • 如下是通用类图中的MyInvacationHandler、DynamicProxy和Client的代码:
    • public class MyInvocationHandler implements InvocationHandler{
          //被代理的实例
          private Object obj = null;
          public MyInvocationHandler(Object obj){
              this.obj = obj;
          }
      
          @Override
          public Object invoke(Object proxy, Method method, Object[] args)
                  throws Throwable {
              // TODO Auto-generated method stub
              Object result = method.invoke(this.obj, args);
              return result;
          }
      }
      
      public class DynamicProxy <T>{
          public static <T> T newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h){
              if(true){
                  (new BeforeAdvice()).exec();
              }
              return (T) Proxy.newProxyInstance(loader, interfaces, h);
          }
      }
      
      public class Client {
      
          /**
           * @param args
           */
          public static void main(String[] args) {
              // TODO Auto-generated method stub
              IGamePlayer player = new GamePlayer();
              MyInvocationHandler handler = new MyInvocationHandler(player);
              IGamePlayer proxy = DynamicProxy.newProxyInstance(player.getClass().getClassLoader(), player.getClass().getInterfaces(), handler);
              proxy.killBoss();
          }
      }

      动态代理调用过程示意图如下:

  • 最佳实践

要实现动态代理的首要条件是:被代理类必须实现一个接口,当然也有很多技术如CGLIB可以再不实现接口的情况下实现动态代理的方式。

原文地址:https://www.cnblogs.com/zhanglei93/p/6045130.html