美文网首页
行为型-代理(Proxy)

行为型-代理(Proxy)

作者: DoneWillianm | 来源:发表于2020-09-22 22:26 被阅读0次

    代理(Proxy)

    [TOC]

    定义

    代理模式主要的实现分为两种,一类是静态代理,一类是动态代理,无论是静态还是动态,只是他们的实现方式不一样,实则核心思想是一致的:

    Provide a surrogate or placeholder for another object to control access to it.(为其他对象提供一种代理以控制对这个对象的访问)

    代理模式的写法和他的思想一样简单,主要为下面三点

    • 业务接口
    • 业务接口的实现者
    • 业务接口的代理者

    其中代理者和实现者实现的接口是一样的,只不过在调用实际能力的时候,是由实现者来完成具体的工作

    简单场景使用

    惯例英雄联盟背景~

    代理游戏上来讲就是代练,这个解释简直形象的亚P,我高中时候玩这款游戏本来是电四的,后来上大学了,大家都是网通,所以我又转网通区了,可是转了网通后得重新练号,不然没法打排位,而升到30级简直艰难的亚P,我希望自己的网通区账号能够尽快达到排位的门槛,这时候代练的作用就来咧并且在这期间,我希望我自己想打两把人机快乐快乐的时候,我能自己上号,我不玩的时候,代练兄弟能上号升级,美滋滋

    coding

    首先我们先定义一波最基本的能力,就是升级(手动笑哭)

    public interface IPlayer {
    
        /**
         * 登录
         *
         * @param userName
         * @param password
         */
        void login(String userName, String password);
    
        /**
         * 游戏中
         */
        void gaming();
    
        /**
         * 升级咧
         */
        void upgrade();
    
    }
    

    接下来是游戏业务的实现类

    public class PlayerImpl implements IPlayer {
    
        private String mName;
    
        private String mUserName;
    
        public PlayerImpl(String name) {
            this.mName = name;
        }
    
        @Override
        public void login(String userName, String password) {
            this.mUserName = userName;
            System.out.println(generateName() + "登录了游戏...");
        }
    
        @Override
        public void gaming() {
            System.out.println(generateName() + "游戏中...");
        }
    
        @Override
        public void upgrade() {
            System.out.println(generateUser() + "升级了...");
        }
    
        private String generateName() {
            return "玩家 [" + mName + "]";
        }
    
        private String generateUser() {
            return "账号 [" + mUserName + "]";
        }
    }
    

    然后就是咱们的代练实现

    public class PlayerProxy implements IPlayer {
    
        private String mUserName;
    
        private IPlayer mRealPlayer;
    
        public PlayerProxy(IPlayer player) {
            this.mRealPlayer = player;
        }
    
        @Override
        public void login(String userName, String password) {
            mUserName = userName;
            checkMoney(userName);
            mRealPlayer.login(userName, password);
        }
    
        @Override
        public void gaming() {
            mRealPlayer.gaming();
        }
    
        @Override
        public void upgrade() {
            mRealPlayer.upgrade();
            checkOrder();
        }
    
        private void checkMoney(String userName) {
            System.out.println(generateUser(userName) + "钱够,给他练级");
        }
    
        private void checkOrder() {
            System.out.println(generateUser(mUserName) + "完成任务,扣他账户余额");
        }
    
        private String generateUser(String userName) {
            return "账号 [" + userName + "]";
        }
    }
    

    main里面模拟一下我自己登号和代练等号的场景

        public static void main(String[] args) {
    
            PlayerImpl player = new PlayerImpl("Done");
            executeGame(player);
    
            System.out.println();
    
            PlayerImpl player1 = new PlayerImpl("工具人1号");
            PlayerProxy proxy1 = new PlayerProxy(player1);
            executeGame(proxy1);
    
        }
    
        private static void executeGame(IPlayer player) {
            String username = "929891705";
            String password = "admin";
            player.login(username, password);
            player.gaming();
            player.upgrade();
        }
    
    //LOG
    //玩家 [Done]登录了游戏...
    //玩家 [Done]游戏中...
    //账号 [929891705]升级了...
    
    //账号 [929891705]钱够,给他练级
    //玩家 [工具人1号]登录了游戏...
    //玩家 [工具人1号]游戏中...
    //账号 [929891705]升级了...
    //账号 [929891705]完成任务,扣他账户余额
    

    可以看到,代练在给我升级之前,会先check一遍我账号的钱够不够,不够就不给我升级了,而游戏能力只需要一个通用的实现即可,非常方便,对上层及其友好,符合迪米特法则

    动态代理

    动态代理其实是借助了java语言的InvocationHandler接口来进行实现,他其实是通过classLoader的去创建实例对象从而完成对代理对象的创建,实际解决的方案则是AOP的手段来完成对扩展开放的能力,这里还是借助上面的场景来完成动态代理的使用

    coding

    首先代练的兄弟需要检测玩家是否正在进行游戏,如果正在进行的话,就不抢登了,等到玩家下线,咱们代理在上线,如果我们既希望代理能完成这项操作,又希望原有的代练代码不做改动,那可以考虑使用动态代理的方式来实现

    实现动态代理主要有两个步骤

    • 实现InvocationHandler接口
    • 通过Proxy.newProxyInstance实例化代理对象
    public class InvocationPlayer implements InvocationHandler {
    
        private IPlayer mPlayer;
    
        private boolean mLogin;
    
        private static final String sName = "Done";
    
        public InvocationPlayer(IPlayer player, boolean isLogin) {
            mPlayer = player;
            mLogin = isLogin;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Object invoke = null;
            try {
                String nickName = mPlayer.getNickName();
                if (!sName.equals(nickName)) {
                    if (mLogin) {
                        System.out.println("玩家[" + sName + "]登录了游戏,一会再登吧");
                    } else {
                        System.out.println("玩家[" + nickName + "]没有登录游戏,可以登录代练");
                        invoke = method.invoke(mPlayer, args);
                    }
                } else {
                    invoke = method.invoke(mPlayer, args);
                }
            } catch (Exception e) {
                e.printStackTrace();
                System.out.println("哦豁,崩逑~");
            }
            return invoke;
        }
    }
    

    实例化代理对象,这里为了验证强登情况,实现两个,一个验证玩家登陆,一个验证代理登陆

    PlayerImpl player2 = new PlayerImpl("工具人2号");
    PlayerProxy proxy2 = new PlayerProxy(player2);
    IPlayer playerDyn2 = (IPlayer) Proxy.newProxyInstance(proxy2.getClass().getClassLoader(),
                                                          proxy2.getClass().getInterfaces(),
                                                          new InvocationPlayer(proxy2, false));
    executeGame(playerDyn2);
    
    System.out.println();
    
    PlayerImpl player3 = new PlayerImpl("工具人3号");
    PlayerProxy proxy3 = new PlayerProxy(player3);
    IPlayer playerDyn3 = (IPlayer) Proxy.newProxyInstance(proxy3.getClass().getClassLoader(),
                                                          proxy3.getClass().getInterfaces(),
                                                          new InvocationPlayer(proxy3, true));
    executeGame(playerDyn3);
    
    //玩家[工具人2号]没有登录游戏,可以登录代练
    //账号 [929891705]钱够,给他练级
    //玩家 [工具人2号]登录了游戏...
    //玩家[工具人2号]没有登录游戏,可以登录代练
    //玩家 [工具人2号]游戏中...
    //玩家[工具人2号]没有登录游戏,可以登录代练
    //账号 [929891705]升级了...
    //账号 [929891705]完成任务,扣他账户余额
    
    //玩家[Done]登录了游戏,一会再登吧
    //玩家[Done]登录了游戏,一会再登吧
    //玩家[Done]登录了游戏,一会再登吧
    
    

    实际场景使用

    最出名的莫过于JakeWharton大佬的retrofit了,感兴趣的同学可以参考下~

    相关文章

      网友评论

          本文标题:行为型-代理(Proxy)

          本文链接:https://www.haomeiwen.com/subject/nxigyktx.html