设计模式-代理模式(Proxy Pattern)

设计模式-代理模式(Proxy Pattern)



一、定义

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

二、概念解释

就是对真实对象的包装,通过包装,不仅能代理实现真实对象提供的服务,还能在提供服务的各个时间节点实现额外的附加功能

三、场景

暗黑破坏神3是一款非常肝的游戏,里面的单个装备最好的就是前缀有太古修饰的,以前玩的时候为了刷太古,不知肝过了多少个春夏秋冬,现在想想这种重复的肝副本我完全可以找个代练帮我肝,这样我能省下大把的时间和留个好肝

四、实现

1、类图

在这里插入图片描述

2、代码实现

首先定义一个游戏接口

public interface DiabloThree {

    /**
     * 刷超级装备
     */
    void getSuperEquipment();
}

我是真实玩家

public class Player implements DiabloThree{

    @Override
    public void getSuperEquipment() {
        System.out.println("不出太古不睡觉");
    }
}

代练

public class PlayerProxy implements DiabloThree{

    private Player player;

    public PlayerProxy(Player player) {
        this.player = player;
    }

    @Override
    public void getSuperEquipment() {

        checkUserCount();
        player.getSuperEquipment();
        recordGetSuperEquipmentDate();
    }

    private void checkUserCount() {

        System.out.println("检查下用户的装备刷不刷的动怪");
    }

    private void recordGetSuperEquipmentDate() {
        System.out.println("记录下时间 好知道平均多少年出一件太古");
    }
}

先自己刷刷到困为止,然后让代练继续刷

@SpringBootTest
public class ProxyTest {

    @Test
    public void test() {

        // 我在刷
        Player player = new Player();
        player.getSuperEquipment();
        // 刷不动了 找代练
        PlayerProxy playerProxy = new PlayerProxy(player);
        // 代练刷
        playerProxy.getSuperEquipment();
    }

}

五、小结

以上例子,代练这个代理类是我通过编码在编译前就定义好了的,这种代理就叫做静态代理,很明显静态代理太局限,当前代练在刷装备前检查了账号强度,在刷装备后做了时间记录,如果有另外一个代练,他在刷装备前是不检查账号强度的,而是检查你有没有足够的刷副本的钥匙,然后刷完装备后也不记录,而是截图,那我就得再硬编码一个这样的代练出来,如果又有别的代练在刷装备前后有不同的操作,那就得再敲代码。这种方式很明显笨拙,那有没有我不用自己写代理类,而是可以根据我的需要在程序运行时自动生成符合条件的代理类,答案是肯定的,这种方式就是动态代理

六、动态代理实现

动态代理常用的实现方式有两种,一种是java通过Proxy类和InvocationHandler接口实现的基于实现接口的动态代理,一种是cglib通过其增强器Enchancer基于继承实现的动态代理,本文仅演示java基于接口实现的动态代理

代理类要执行的方法必须实现InvocationHandler接口中的invoke方法 这里我们针对上述的游戏例子 生成一个检查账号的invocationHandler

public class CheckPlayerAccountInvocationHandler<T> implements InvocationHandler {

    T target;

    public CheckPlayerAccountInvocationHandler(T target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        checkUserCount();
        Object o = method.invoke(target, args);
        recordGetSuperEquipmentDate();
        return o;
    }

    private void checkUserCount() {

        System.out.println("检查下用户的装备刷不刷的动怪");
    }

    private void recordGetSuperEquipmentDate() {
        System.out.println("记录下时间 好知道平均多少年出一件太古");
    }
}

使用Proxy类动态生成代理类

@Test
public void DynamicProxyTest() {

    // 真实玩家
    Player player = new Player();
    // 代理类定义的代理方法
    CheckPlayerAccountInvocationHandler<Player> usePlayerAccountInvocationHandler = new CheckPlayerAccountInvocationHandler<>(player);
    // newProxyInstance生成代理类
    // 第一个参数指定使用哪个类加载器将代理类加载到JVM 一般选择跟被代理对象一样的类加载器
    // 第二个参数指定代理类要实现的接口列表
    // 第三个参数将方法调用分派到自己实现的调用处理程序
    DiabloThree proxy = (DiabloThree) Proxy.newProxyInstance(player.getClass().getClassLoader(), new Class<?>[]{DiabloThree.class}, usePlayerAccountInvocationHandler);
    proxy.getSuperEquipment();
}

六 总结

在实际的开发项目中,该模式最好的体现就是Spring的aop,基于动态代理,aop可以为我们写的接口做统一鉴权、统一日志记录等等,而不需要我们在每一个接口中去做这种重复的逻辑,给我们的开发工作提供了极大的便利

代码地址: https://github.com/LuckyTeemo/design-pattern

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值