zoukankan      html  css  js  c++  java
  • 静态代理和动态代理

    代理模式主要应用的场景是:当某些类由于一些原因不方便直接访问或者修改,需要通过一个代理类作为桥梁,来实现间接访问并扩展功能。

    静态代理

    假设我们有一个游戏模块,包含各种不同类型的游戏,我们需要在游戏开始和结束的时候加入提示,让我们看看利用静态代理怎么实现这个需求:

    上面的UML图定义了一个游戏接口(Game),格斗游戏类(FightingGame)和射击游戏类(ShootingGame)分别实现了该接口,代码如下: ```java public interface Game { public void playGame(); } ``` ```java public class FightingGame implements Game { public void playGame() { System.out.println("FightingGame"); } } ``` ```java public class ShootingGame implements Game { public void playGame() { System.out.println("ShootingGame"); } } ``` 假如我们要在游戏开始前打印"Game Start",在游戏结束时打印"Game Over",为此我们定义一个代理类(GameProxy): ```java public class GameProxy implements Game {
    private Game game;
    
    public GameProxy(Game game) {
        this.game = game;
    }
    
    public void playGame() {
        if(game!=null) {
            System.out.println("Game Start!");
            game.playGame();
            System.out.println("Game Over!");
        }
    }
    

    }

    新建一个测试类:
    ```java
    public class MyTestClass {
    
        @Test
        public void demo01() {
            ShootingGame shootingGame = new ShootingGame();
            Game game = new GameProxy(shootingGame);
            game.playGame();
        }
    
        @Test
        public void demo02() {
            FightingGame fightingGame = new FightingGame();
            Game game = new GameProxy(fightingGame);
            game.playGame();
        }
    }
    

    运行两个测试方法,将分别打印出:

    Game Start!
    FightingGame
    Game Over!
    
    Game Start!
    ShootingGame
    Game Over!
    

    动态代理

    我们用另一个游戏场景来演示动态代理的使用(∩_∩)。
    在LOL里有一个英雄是狂野女猎手(俗称豹女),她有人形和猎豹两种形态,每种形态下都有对应的技能,UML如图:

    上面的UML图定义了人形态(Human)和猎豹形态(Leopard)两种接口,女猎手类(Huntress)同时实现了这两种接口,代码如下:

    public interface Human {
    
        /** 变身为猎豹 */
        public void transformIntoLeopard();
    
        /** 向目标投掷标枪,返回伤害值 */
        public int throwJavelin(String enemy);
    
        /** 在指定位置放置陷阱 */
        public void setTrap(int x, int y);
    }
    
    public interface Leopard {
    
        /** 变身为人形 */
        public void transformIntoHuman();
    
        /** 爪击,返回伤害值 */
        public int clawAttack();
    }
    
    public class Huntress implements Human, Leopard {
        public void transformIntoLeopard() {
            System.out.println("变身为猎豹");
        }
    
        public int throwJavelin(String enemy) {
            System.out.println("对" + enemy + "造成100点伤害");
            return 100;
        }
    
        public void setTrap(int x, int y) {
            System.out.println("在(" + x + ", " + y + ")处放置了一个陷阱");
        }
    
        public void transformIntoHuman() {
            System.out.println("变身为人形");
        }
    
        public int clawAttack() {
            System.out.println("对前方敌人共造成200点伤害");
            return 200;
        }
    }
    

    不同于静态代理需要建立实体代理类,我们直接在测试模块用代码创建动态代理:

    public class MyTestClass {
        @Test
        public void demo01() {
            Huntress huntress = new Huntress();
    
            Object proxy = Proxy.newProxyInstance(
                    huntress.getClass().getClassLoader(),
                    Huntress.class.getInterfaces(),
                    new HuntressInvocationHandler(huntress));
    
            Human humanProxy = (Human)proxy;
            humanProxy.transformIntoLeopard();
            humanProxy.setTrap(25,50);
            humanProxy.throwJavelin("武器大师");
    
            Leopard leopardProxy = (Leopard)proxy;
            leopardProxy.transformIntoHuman();
            leopardProxy.clawAttack();
        }
    }
    

    Proxy.newProxyInstance方法的声明如下:

    public static Object newProxyInstance(ClassLoader loader,
                                              Class<?>[] interfaces,
                                              InvocationHandler h);
    

    loader -- 指定用哪个类加载器去加载代理类
    interfaces -- 代理类需要实现的接口列表,在本例中length是2
    h -- 可简单理解为"方法调用处理器实例",InvocationHandler是JDK中专门用于实现动态代理的接口,它有一个invoke方法去处理代理实例上的方法调用并返回结果

    下面的代码定义了一个HuntressInvocationHandler,它继承自InvocationHandler:

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.util.Arrays;
    
    public class HuntressInvocationHandler implements InvocationHandler {
    
        public Huntress huntress;
        public HuntressInvocationHandler(Huntress huntress) {
            this.huntress = huntress;
        }
    
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("------------------------------------------");
            System.out.println("当前方法:" + method.getName());
            String argsString = Arrays.toString(args);
            System.out.println("当前参数:" + argsString);
    
            System.out.println("*****开始施放技能!*****");
            Object result = method.invoke(huntress, args);
            System.out.println("*****结束施放技能!*****");
            if(result!=null) {
                System.out.println("*****造成"+result+"点伤害*****");
            }
    
            return result;
        }
    }
    

    invoke方法的声明如下:

        public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable;
    

    proxy -- 代理类本身的一个实例。这个参数大部分情况下不需要关注。
    method -- 当前调用的方法
    args -- 当前调用方法的参数列表

    运行测试方法,将打印下列信息:

    --------------------------------------------------------
    当前方法:transformIntoLeopard
    当前参数:null
    *****开始施放技能!*****
    变身为猎豹
    *****结束施放技能!*****
    --------------------------------------------------------
    当前方法:setTrap
    当前参数:[25, 50]
    *****开始施放技能!*****
    在(25, 50)处放置了一个陷阱
    *****结束施放技能!*****
    --------------------------------------------------------
    当前方法:throwJavelin
    当前参数:[武器大师]
    *****开始施放技能!*****
    对武器大师造成100点伤害
    *****结束施放技能!*****
    *****造成100点伤害*****
    --------------------------------------------------------
    当前方法:transformIntoHuman
    当前参数:null
    *****开始施放技能!*****
    变身为人形
    *****结束施放技能!*****
    --------------------------------------------------------
    当前方法:clawAttack
    当前参数:null
    *****开始施放技能!*****
    对前方敌人共造成200点伤害
    *****结束施放技能!*****
    *****造成200点伤害*****
    

    总结

    静态代理要求代理类和委托类实现同一个接口,代理类在编译期生成,效率高。相应缺点是会多出一些代理类。
    动态代理不要求代理类和委托类实现同一个接口(没有实现接口的类无法使用动态代理),它是在程序运行时根据需要动态创建目标类的代理对象,但由于它是通过反射来代理方法,在性能上会有所消耗。

  • 相关阅读:
    简直喝血!H.265要被专利费活活玩死
    python异常处理
    开始好好总结所学东西了,马上校招了~~
    pip 安装管理失败解决
    去除空格
    自定义开关(不带有滑动,只具有点击切换开关功能)
    Android-ViewPagerIndicator框架使用——TabPageIndicator以及样式的修改
    Android详细的对话框AlertDialog.Builder使用方法
    Fragment+FragmentTabHost组件实现常见主页面(仿微信新浪)
    Activity生命周期
  • 原文地址:https://www.cnblogs.com/CoderWayne/p/11496415.html
Copyright © 2011-2022 走看看