代理模式 (ProxyPattern)

代理模式 (ProxyPattern)

代理模式的定义

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

代理模式的优点

  • 真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的事务,通过后期的代理完成一件事务,附带的结果就是编程简洁清晰。
  • 具体主题角色是随时都会发生变化的,只要它实现了接口,甭管它如何变化,都逃不脱如来佛的手掌(接口),那我们的代理类完全就可以在不做任何修改的情况下使用。
  • 这在我们以上的讲解中还没有体现出来,不过在我们以下的动态代理章节中你就会看到代理的智能化有兴趣的读者也可以看看Struts是如何把表单元素映射到对象上的。
  • 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
  • 代理对象可以扩展目标对象的功能;
  • 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度,增加了程序的可扩展性

代理模式的缺点

  • 代理模式会造成系统设计中类的数量增加
  • 在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢;
  • 增加了系统的复杂度;

代理模式的结构

代理模式的主要角色如下。

  1. 抽象主题(Subject)类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。
  2. 真实主题(Real Subject)类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。
  3. 代理(Proxy)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。

代理模式的实现

普通代理

客户端只能访问代理角色,而不能访问真实角色。

public interface IGamePlayer {

    /**
     * 登录游戏
     */
    public void login(String user, String password);

    /**
     * 杀怪,网络游戏的主要特色
     */
    public void killBoss();

    /**
     * 升级
     */
    public void upgrade();
}
public interface IProxy {
    /**
     * 代理服务费
     */
    public void count();
}
public class GamePlayer implements IGamePlayer {

    private String name = "";

    /**
     * 构造函数限制谁能创建对象,并同时传递姓名
     */
    public GamePlayer(IGamePlayer gamePlayer, String name) throws Exception {
        if (gamePlayer == null) {
            throw new Exception("不能创建真实角色!");
        } else {
            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 + "又升了一级!");
    }
}
public class GamePlayerProxy implements IGamePlayer, IProxy {

    private IGamePlayer gamePlayer = null;

    /**
     * 通过构造函数传递要对谁进行代练
     */
    public GamePlayerProxy(String name) {
        try {
            gamePlayer = new GamePlayer(this, name);
        } catch (Exception e) {
            // TODO 异常处理
        }
    }

    /**
     * 登录游戏
     */
    @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();
        this.count();
    }

    /**
     * 代理服务费
     */
    @Override
    public void count() {
        System.out.println("升级总费用是:150元");
    }
}
public class Client {
    public static void main(String[] args) {
        //然后再定义一个代练者
        IGamePlayer proxy = new GamePlayerProxy("张三");
        //开始打游戏,记下时间戳
        System.out.println("开始");

        proxy.login("zhangSan", "password");
        //开始杀怪
        proxy.killBoss();
        //升级
        proxy.upgrade();
        //记录结束游戏时间
        System.out.println("结束");
    }
}

强制代理

必须通过真实角色查找到代理角色,否则你不能访问。

public interface IGamePlayer {
    /**
     * 登录游戏
     */
    public void login(String user, String password);

    /**
     * 杀怪,网络游戏的主要特色
     */
    public void killBoss();

    /**
     * 升级
     */
    public void upgrade();

    /**
     * 每个人都可以找一下自己的代理
     *
     * @return
     */
    public IGamePlayer getProxy();
}
public class GamePlayer implements IGamePlayer {

    private String name = "";

    /**
     * 我的代理是谁
     */
    private IGamePlayer proxy = null;

    public GamePlayer(String name) {
        this.name = name;
    }

    /**
     * 登录游戏
     */
    @Override
    public void login(String user, String password) {
        if (this.isProxy()) {
            System.out.println("登录名为" + user + "的用户" + this.name + "登录成功!");
        } else {
            this.mustProxy();
        }
    }

    /**
     * 杀怪,网络游戏的主要特色
     */
    @Override
    public void killBoss() {
        if (this.isProxy()) {
            System.out.println(this.name + "在打怪!");
        } else {
            this.mustProxy();
        }
    }

    /**
     * 升级
     */
    @Override
    public void upgrade() {
        if (this.isProxy()) {
            System.out.println(this.name + "又升了一级!");
        } else {
            this.mustProxy();
        }

    }

    public void mustProxy() {
        System.out.println("请使用指定的代理访问");
    }

    /**
     * 每个人都可以找一下自己的代理
     *
     * @return
     */
    @Override
    public IGamePlayer getProxy() {
        this.proxy = new GamePlayerProxy(this);
        return this.proxy;
    }

    /**
     * 校验是否是代理访问
     */
    private boolean isProxy() {
        return this.proxy != null;
    }
}
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();
    }

    /**
     * 每个人都可以找一下自己的代理
     */
    @Override
    public IGamePlayer getProxy() {
        return this;
    }
}
public class Client {
    public static void main(String[] args) {
        //定义一个游戏的角色
        IGamePlayer player = new GamePlayer("张三");
        IGamePlayer playerProxy = player.getProxy();
        //开始打游戏,记下时间戳
        System.out.println("开始");
        playerProxy.login("zhangSan", "password");
        //开始杀怪
        playerProxy.killBoss();
        //升级
        playerProxy.upgrade();
        //记录结束游戏时间
        System.out.println("结束");

    }
}

动态代理

动态代理是在实现阶段不用关心代理谁,而在运行阶段才指定代理哪一个对象。

public interface IGamePlayer {

    /**
     * 登录游戏
     */
    public void login(String user, String password);

    /**
     * 杀怪,网络游戏的主要特色
     */
    public void killBoss();

    /**
     * 升级
     */
    public void upgrade();
}
public class GamePlayIH implements InvocationHandler {
    /**
     * 被代理者
     */
    Class cls = null;
    /**
     * 被代理的实例
     */
    Object obj = null;

    /**
     * 我要代理谁
     *
     * @param obj
     */
    public GamePlayIH(Object obj) {
        this.obj = obj;
    }

    /**
     * 调用被代理的方法
     *
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        Object result = method.invoke(this.obj, args);
        if (method.getName().equalsIgnoreCase("login")) {
            System.out.println("有人在用我的账号登录!");
        }
        return result;
    }
}
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 + "又升了一级!");
    }
    
}
public class Client {
    public static void main(String[] args) throws Exception {
        //定义一个痴迷的玩家
        IGamePlayer player = new GamePlayer("张三");
        //定义一个handler
        InvocationHandler handler = new GamePlayIH(player);
        //开始打游戏,记下时间戳
        System.out.println("开始");
        //获得类的class loader
        ClassLoader cl = player.getClass().getClassLoader();
        //动态产生一个代理者
        IGamePlayer proxy = (IGamePlayer) Proxy.newProxyInstance(cl, new Class[]{IGamePlayer.class}, handler);
        proxy.login("zhangSan", "password");
        //开始杀怪
        proxy.killBoss();
        //升级
        proxy.upgrade();
        //记录结束游戏时间
        System.out.println("结束");
    }
}
AOP
public interface Subject {
    /**
     * 业务操作
     */
    public void doSomething(String str);
}
public class RealSubject implements Subject {
    /**
     * 业务操作
     */
    @Override
    public void doSomething(String str) {
        System.out.println("do something!---->" + str);
    }
}
public interface IAdvice {
    /**
     * 通知只有一个方法,执行即可
     */
    public void exec();
}
public class BeforeAdvice implements IAdvice {
    @Override
    public void exec() {
        System.out.println("我是前置通知,我被执行了!");
    }
}
public class MyInvocationHandler implements InvocationHandler {
    /**
     * 被代理的对象
     */
    private Object target = null;

    /**
     * 通过构造函数传递一个对象
     */
    public MyInvocationHandler(Object obj) {
        this.target = obj;
    }

    /**
     * 代理方法
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //执行被代理的方法
        return method.invoke(this.target, args);
    }
}
public class DynamicProxy {
    @SuppressWarnings("unchecked")
    public static <T> T newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) {
        //寻找JoinPoint连接点,AOP框架使用元数据定义
        if (true) {
            //执行一个前置通知
            (new BeforeAdvice()).exec();
        }
        //执行目标,并返回结果
        return (T) Proxy.newProxyInstance(loader, interfaces, h);
    }
}
public class SubjectDynamicProxy extends DynamicProxy {
    public static <T> T newProxyInstance(Subject subject) {
        //获得ClassLoader
        ClassLoader loader = subject.getClass().getClassLoader();
        //获得接口数组
        Class<?>[] classes = subject.getClass().getInterfaces();
        //获得handler
        InvocationHandler handler = new MyInvocationHandler(subject);
        return newProxyInstance(loader, classes, handler);
    }
}
public class Client {
    public static void main(String[] args) {
        c1();
        c2();
    }

    public static void c1() {
        //定义一个主题
        Subject subject = new RealSubject();
        //定义一个Handler
        InvocationHandler handler = new MyInvocationHandler(subject);
        //定义主题的代理
        Subject proxy = DynamicProxy.newProxyInstance(subject.getClass().
                getClassLoader(), subject.getClass().getInterfaces(), handler);
        //代理的行为
        proxy.doSomething("Finish1");
    }

    public static void c2() {
        //定义一个主题
        Subject subject = new RealSubject();
        //定义主题的代理
        Subject proxy = SubjectDynamicProxy.newProxyInstance(subject);
        //代理的行为
        proxy.doSomething("Finish2");
    }
}

使用场景

当无法或不想直接引用某个对象或访问某个对象存在困难时,可以通过代理对象来间接访问。使用代理模式主要有两个目的:一是保护目标对象,二是增强目标对象。

  • 远程代理,这种方式通常是为了隐藏目标对象存在于不同地址空间的事实,方便客户端访问。例如,用户申请某些网盘空间时,会在用户的文件系统中建立一个虚拟的硬盘,用户访问虚拟硬盘时实际访问的是网盘空间。
  • 虚拟代理,这种方式通常用于要创建的目标对象开销很大时。例如,下载一幅很大的图像需要很长时间,因某种计算比较复杂而短时间无法完成,这时可以先用小比例的虚拟代理替换真实的对象,消除用户对服务器慢的感觉。
  • 安全代理,这种方式通常用于控制不同种类客户对真实对象的访问权限。
  • 智能指引,主要用于调用目标对象时,代理附加一些额外的处理功能。例如,增加计算真实对象的引用次数的功能,这样当该对象没有被引用时,就可以自动释放它。
  • 延迟加载,指为了提高系统的性能,延迟对目标的加载。例如,Hibernate中就存在属性的延迟加载和关联表的延时加载。

本文主要参考:

  1. 小傅哥的《重学Java模式》
  2. 《C语言中文网》设计模式的相关内容
  3. 《设计模式之禅》第二版 秦小波
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

顧棟

若对你有帮助,望对作者鼓励一下

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值