一, 什么是代理模式
关于代理模式, 我们听到的见到的最多的可能就是静态代理, 动态代理之类的, 当然还有大家都知道的 Spring Aop, 这里我们先不谈这些个代理, 先说个简单的例子. 游戏代练应该都听说过, 许多人肯定也找过代练, 曾经 DNF,LOL,COC 等等游戏的代练很多, 当然现在各类游戏层出不穷, 也都有各种代练, 那这里所谓的代练是什么? 就是 Proxy, 也即代理类, 那游戏代练这件事就是一个代理模式.
如果觉得不好理解可以这么想, 代练的流程是, 你把自己的账号交给代练人员, 让他们帮你打怪升级, 而你只需要提供账号即可. 那代练人员那边, 他所要做的就是登陆你的账号, 然后替你打游戏, 从第三者的角度来看, 你这个角色在打怪升级, 但这个第三者并不知道是不是你本人在打游戏, 他只能看到你这个账号正在打怪升级, 但并不需要知道后面打游戏的是谁. 这就是代理模式, 由他人代理玩游戏.
如果觉得这个还不好理解, 那再说一个例子. 假设我现在要邀请明星来上节目, 我是直接给这个明星打电话吗? 当然不是, 是给他的经纪人打电话, 然后再由经纪人通知到该明星, 这里经纪人充当的就是代理的角色.
更常见的例子就是 Windows 的快捷方式, 通过快捷方式, 我们可以访问某个文件夹下的 exe 文件, 这就是一个典型的代理模式, 它将接口, 按上面游戏的说法说就是代练的账号, 提供了出来, 我们只需点击快捷方式, 它会帮我们运行指定目录下的指定程序. 说了这么多, 现在来看一下代理模式的定义.
代理模式(Proxy), 为其他对象提供一种代理以控制对这个对象的访问. UML 结构图如下:
其中, Subject 是主题角色, 定义了 RealSubject 和 Proxy 的共同接口; RealSubject 是具体主题角色, 定义了 Proxy 所代表的真实实体; Proxy 为代理主题角色, 保存一个引用使代理可以访问实体, 并提供一个与 Subject 的接口相同的接口.
1. Subject 抽象类
定义了 RealSubject 和 Proxy 的共同接口, 这样就在任何使用 RealSubject 的地方都可以使用 Proxy.
- public abstract class Subject {
- public abstract void request();
- }
2. RealSubject 类
定义了 Proxy 所代表的真实实体.
- public class RealSubject extends Subject {
- @Override
- public void request() {
- System.out.println("真实的请求 RealSubject");
- }
- }
3. Proxy 类
代理类. 一个代理类可以代理多个被委托者或被代理者, 因此一个代理类具体代理哪个真实主题角色, 是由场景类决定的.
- public class Proxy extends Subject {
- private RealSubject realSubject = null;
- public Proxy() {
- this.realSubject = new RealSubject();
- }
- @Override
- public void request() {
- this.before();
- this.realSubject.request();
- this.after();
- }
- // 预处理
- private void before() {
- System.out.println("-------before------");
- }
- // 善后处理
- private void after() {
- System.out.println("-------after-------");
- }
- }
4. Client 客户端
- public class Client {
- public static void main(String[] args) {
- Proxy proxy = new Proxy();
- proxy.request();
- }
- }
运行结果如下:
这个结果和 Spring 的 AOP 很类似, 可以相互对照一下.
二, 代理模式的应用
1. 何时使用
想在访问一个类的时候做一些控制时
2. 方法
增加中间层
3. 优点
职责清晰. 真实的角色就是实现实际的业务逻辑, 不用担心其他非本职责的事务
高扩展性. 代理类完全可以在不做任何修改的情况下使用
智能化. 比如动态代理
4. 缺点
有些类型的代理模式可能会造成请求的处理速度变慢
实现代理模式需要额外的工作, 有些代理模式的实现非常复杂
5. 使用场景
远程代理. 为一个对象在不同的地址空间提供局部代表
虚拟代理. 根据需要创建开销很大的对象, 通过它来存放实例化需要很长时间的真实对象
安全代理. 用来控制真实对象访问时的权限
智能指引, 当调用真实的对象时, 代理处理另外一些事
6. 应用实例
游戏代练
邀请明星, 联系其经纪人
Windows 快捷方式
火车票代售点
Spring AOP
7. 注意事项
与适配器模式的区别: 适配器模式主要改变所考虑对象的接口, 而代理模式不能改变所代理类的接口
与装饰模式区别: 装饰模式是为了增强功能, 而代理模式是为了加以控制
三, 代理模式的实现
这里我们就以上面说过的游戏代练为例. UML 图如下:
1. IGamePlayer 类
游戏玩家接口. 由登陆, 杀怪, 升级三个方法.
- public interface IGamePlayer {
- // 登陆
- public void login(String user, String password);
- // 杀怪
- public void killBoss();
- // 升级
- public void upgrade();
- }
2. GamePlayer 类
游戏玩家. 正常玩家登陆, 打怪, 升级.
- public class GamePlayer implements IGamePlayer {
- private String name = "";
- public GamePlayer(String name) {
- this.name = name;
- }
- @Override
- public void login(String user, String password) {
- System.out.println("登录名为 <" + user + "> 的用户 <" + this.name + "> 登陆成功!");
- }
- @Override
- public void killBoss() {
- System.out.println("<" + this.name + ">在打怪!");
- }
- @Override
- public void upgrade() {
- System.out.println("<" + this.name + ">升了一级!");
- }
- }
3. GamePlayerProxy 类
游戏代练. 也即代理类, 用于代替玩家登陆, 打怪, 升级.
- public class GamePlayerProxy implements IGamePlayer {
- private IGamePlayer gamePlayer = null;
- public GamePlayerProxy(IGamePlayer gamePlayer) {
- this.gamePlayer = gamePlayer;
- }
- @Override
- public void login(String user, String password) {
- this.gamePlayer.login(user, password);
- }
- @Override
- public void killBoss() {
- this.gamePlayer.killBoss();
- }
- @Override
- public void upgrade() {
- this.gamePlayer.upgrade();
- }
- }
4. Client 客户端
玩家找了一个代练, 代练登陆了该玩家的账号, 然后替该玩家打怪升级.
- public class Client {
- public static void main(String[] args) {
- // 定义一个玩家
- IGamePlayer player = new GamePlayer("桐人");
- // 定义一个代练
- IGamePlayer proxy = new GamePlayerProxy(player);
- // 开始打游戏
- // 登陆
- proxy.login("adam", "123456");
- // 开始杀怪
- proxy.killBoss();
- // 升级
- proxy.upgrade();
- }
- }
运行结果如下:
从这个结果我们只能看到有人登陆了这个账号, 他正在打怪升级, 但并不知道是不是代练, 这就是代理模式.
源码地址: https://gitee.com/adamjiangwh/GoF
来源: https://www.cnblogs.com/adamjwh/p/9102037.html