行为型-代理(Proxy)

代理(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))