• 代理模式



    代理模式


    • 代理模式也被称为委托模式,为其他对象提供一种代理以控制对这个对象的访问(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可以再不实现接口的情况下实现动态代理的方式。

  • 相关阅读:
    【深入理解 Java 虚拟机】2. 自动内存管理
    【深入理解 Java 虚拟机】1. 走进 Java
    【JVM】体系结构和底层原理
    【JVM】JVM 入门与基础总结
    ios 个推推送集成
    iOS UIWebView 和 WKWebView 的 cookie 获取,设置,删除
    CUICatalog: Invalid asset name supplied: '(null)'
    iOS 警告收集快速消除
    ios 你必须了解的系统定义宏使用
    ios 开发常用函数
  • 原文地址:https://www.cnblogs.com/zhanglei93/p/6045130.html
Copyright © 2020-2023  润新知