努力保头发的打工人
努力保头发的打工人
发布于 2020-12-30 / 24 阅读 / 0 评论 / 0 点赞

代理模式

1 概述

1.1定义

为其他对象提供一种代理以控制对这个对象的访问。

1.2 类图

代理模式类图.png

  • subject:抽象主题类,可以是接口也可以是抽象类。

  • realsubject:具体主题类,被代理的类,真实角色。是业务逻辑的具体执行者。

  • proxy:代理类,它与被他代理的类实现同样接口,但是具体方法由具体主题类执行,而且在具体主题类处理前后做预处理和善后处理。

1.3代理模式优点
  • 职责清晰:真实角色就只实现实际的业务逻辑,不用关心其他非本职的事务。

  • 高扩展性:真实角色不管如何变化,因为实现了固定接口,代理类完全不需要做任何修改。

  • 智能化:

1.4 代理模式扩展细分
1.4.1 普通代理

要求客户端只能访问代理角色,不能访问真实角色。以玩家打怪升级,找代练为例,玩家自己不打怪,交由代练来进行。

/**
 * @author chiangtaol
 * @date 2020-11-30
 * @describe 游戏玩家接口类,真实玩家与代练都属于游戏玩家,实现相同接口
 */
public interface IGamePlayer {
​
    /**
     * 登录
     *
     * @param name     名称
     * @param password 密码
     */
    void login(String name, String password);
​
    /**
     * 杀怪
     */
    void killBoss();
​
    /**
     * 升级
     */
    void upgrade();
}
​
/**
 * @author chiangtaol
 * @date 2020-11-30
 * @describe 真实游戏玩家
 */
public class GamePlayer implements IGamePlayer {
    /**
     * 登录
     *
     * @param name     名称
     * @param password 密码
     */
    @Override
    public void login(String name, String password) {
        System.out.println(String.format("玩家登录,账号:%s,密码%s",name,password));
    }
​
    /**
     * 杀怪
     */
    @Override
    public void killBoss() {
        System.out.println("击杀怪物.");
    }
​
    /**
     * 升级
     */
    @Override
    public void upgrade() {
        System.out.println("等级成功+1");
    }
}
​
/**
 * @author chiangtaol
 * @date 2020-11-30
 * @describe 代练玩家,打怪升级在玩家原有方法上加入时间
 */
public class GamePlayProxy implements IGamePlayer {
​
    private IGamePlayer gamePlayer;
​
    public GamePlayProxy(IGamePlayer iGamePlayer) {
        this.gamePlayer = iGamePlayer;
    }
​
    /**
     * 登录
     *
     * @param name     名称
     * @param password 密码
     */
    @Override
    public void login(String name, String password) {
        this.showTime();
        this.gamePlayer.login(name, password);
    }
​
    /**
     * 杀怪
     */
    @Override
    public void killBoss() {
        this.showTime();
        this.gamePlayer.killBoss();
    }
​
    /**
     * 升级
     */
    @Override
    public void upgrade() {
        this.showTime();
        this.gamePlayer.upgrade();
    }
​
    private void showTime(){
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println(format.format(new Date()));
    }
}
1.4.2 强制代理

一般都是通过代理类找到真实角色,但是强制代理却要强制你必须通过真实角色找到代理角色,否则不能访问。例如:你要去找一个公司总裁去处理事情,总裁一般不会自己来,都是交由自己的秘书来处理。秘书就是代理类,每个总裁需要指定自己的秘书。我们上面玩游戏的例子就可以使用下面代码。

/**
 * @author chiangtaol
 * @date 2020-12-01
 * @describe 玩家抽象类
 */
public interface IGameForcePlayer {
​
    /**
     * 登录
     * @param name 名称
     * @param password 密码
     */
    void login(String name,String password);
​
    /**
     * 杀怪
     */
    void killBoss();
​
​
    /**
     * 升级
     */
    void upgrade();
​
    /**
     * 获取自己的代理类
     * @return /
     */
    IGameForcePlayer getProxy();
  
  
  /**
 * @author chiangtaol
 * @date 2020-12-01
 * @describe 游戏玩家
 */
public class GameForcePlayer implements IGameForcePlayer {
​
    /** 名称*/
    private String name;
​
    /** 我的代理*/
    private IGameForcePlayer proxy;
​
    public GameForcePlayer(String name){
        this.name = name;
    }
​
    @Override
    public IGameForcePlayer getProxy(){
        this.proxy = new GameForceProxy(this);
        return this.proxy;
    }
    /**
     * 登录
     *
     * @param name     名称
     * @param password 密码
     */
    @Override
    public void login(String name, String password) {
        if (this.isProxy()){
            System.out.println(String.format("玩家登录,账号:%s,密码%s",name,password));
        }else {
            System.out.println("请使用代理访问");
        }
​
    }
​
    /**
     * 杀怪
     */
    @Override
    public void killBoss() {
        if (this.isProxy()){
            System.out.println("击杀怪物.");
        }else {
            System.out.println("请使用代理访问");
        }
    }
​
    /**
     * 升级
     */
    @Override
    public void upgrade() {
        if (this.isProxy()){
            System.out.println("等级成功+1");
        }else {
            System.out.println("请使用代理访问");
        }
    }
​
​
    /**
     * 检验是否是代理访问
     * @return /
     */
    public boolean isProxy(){
        return this.proxy != null;
    }
  
  /**
 * @author chiangtaol
 * @date 2020-12-01
 * @describe 代理类
 */
public class GameForceProxy implements IGameForcePlayer {
​
    /** 自己的代理*/
    private IGameForcePlayer gamePlayer;
​
    /** 获取自己的代理*/
    @Override
    public IGameForcePlayer getProxy(){
        return this;
    }
​
    /** 构造方法指定要代理谁*/
    public GameForceProxy(IGameForcePlayer gamePlayer){
        this.gamePlayer = gamePlayer;
    }
​
    /**
     * 登录
     *
     * @param name     名称
     * @param password 密码
     */
    @Override
    public void login(String name, String password) {
        this.showTime();
        this.gamePlayer.login(name, password);
    }
​
    /**
     * 杀怪
     */
    @Override
    public void killBoss() {
        this.showTime();
        this.gamePlayer.killBoss();
    }
​
    /**
     * 升级
     */
    @Override
    public void upgrade() {
        this.showTime();
        this.gamePlayer.upgrade();
    }
​
    private void showTime(){
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println(format.format(new Date()));
    }
}
​
  class IGameForcePlayerTest {
​
    @Test
    public void test() throws Exception{
        // 直接使用玩家,不能访问
        GameForcePlayer gameForcePlayer = new GameForcePlayer("张三");
        gameForcePlayer.login("张三","password");
        gameForcePlayer.killBoss();
        gameForcePlayer.upgrade();
​
        // 自行创建一个代理,也不能访问
        GameForceProxy proxy = new GameForceProxy(gameForcePlayer);
        proxy.login("张三","password");
        proxy.killBoss();
        proxy.upgrade();
    }
​
    /**
     * 通过玩家获取代理
     */
    @Test
    public void test2(){
        // 获取一个玩家的代理
        IGameForcePlayer proxy = new GameForcePlayer("张三").getProxy();
        // 代理干活
        proxy.login("张三","password");
        proxy.killBoss();
        proxy.upgrade();
    }
}
1.4.3 动态代理

动态代理是在实现阶段不关心代理谁,而是在运行阶段才指定代理哪一个对象。前面两个自己写代理类都是静态代理,动态代理才是最多使用,比较重要的。aop面向切面编程就是动态代理。

下面为一个简单实例,将普通代理模式里的代理类修改下,实现java里InvocationHandler接口,重写invoke方法

/**
 * @author chiangtaol
 * @date 2020-12-01
 * @describe 动态代理类
 */
public class GamePlayIH implements InvocationHandler {
​
    private Class aClass;
​
    /** 被代理者*/
    private Object object;
​
    /**
     * 构造方法
     * @param o 被代理的对象
     */
    public GamePlayIH(Object o){
        this.object = o;
    }
​
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = method.invoke(this.object,args);
        System.out.println(method.getName()+showTime());
        return result;
    }
​
    private String showTime(){
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return format.format(new Date());
    }
}
​
class GamePlayIHTest {
    @Test
    public void testDynamic(){
        // 定义一个玩家
        IGamePlayer player = new GamePlayer();
        // 定义一个handler
        InvocationHandler handler = new GamePlayIH(player);
        // 获取类加载器
        ClassLoader classLoader = player.getClass().getClassLoader();
        // 获取接口
        Class<?>[] interfaces = player.getClass().getInterfaces();
        // 动态产生一个代理者
        IGamePlayer gamePlayer= (IGamePlayer)Proxy.newProxyInstance(classLoader, interfaces, handler);
        gamePlayer.login("name","password");
        gamePlayer.killBoss();
    }
}

这样只是一个简单的动态代理,看起来和静态代理一样,都是需要写一个代理类。我们改造下生成动态代理对象代码

/**
 * @author chiangtaol
 * @date 2020-12-01
 * @describe 通用动态代理类
 */
public class DynamicProxy<T> {
    public static <T> T newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler invocationHandler){
        // 执行一个通知
        System.out.println("动态代理被执行了");
        return (T) Proxy.newProxyInstance(loader,interfaces,invocationHandler);
    }
}
​
/**
 * @author chiangtaol
 * @date 2020-12-01
 * @describe 玩家动态代理类
 */
public class GamePlayDynamicProxy extends DynamicProxy{
​
    public static <T> T newProxyInstance(IGamePlayer player){
        // 定义一个handler
        InvocationHandler handler = new GamePlayIH(player);
        // 获取类加载器
        ClassLoader classLoader = player.getClass().getClassLoader();
        // 获取接口
        Class<?>[] interfaces = player.getClass().getInterfaces();
        return newProxyInstance(classLoader,interfaces,handler);
    }
}
​
class GamePlayIHTest {
    @Test
    public void test() throws Exception{
        // 定义一个玩家
        IGamePlayer gamePlayer = new GamePlayer();
        // 获取玩家代理
        IGamePlayer proxy = GamePlayDynamicProxy.newProxyInstance(gamePlayer);
        proxy.login("test","test");
        proxy.killBoss();
        proxy.upgrade();
    }
​
}

这样改造,高层模块调用更加容易。这样与静态代理区别主要在DynamicProxy,可以理解为横向切面编程,在不影响已有代码情况对所有使用该子类代理的方法进行了增强或控制。