目录

  • 代理模式
    • 例子
    • 定义
    • 优点
    • 使用场景
    • 扩展
      • 普通代理
      • 强制代理
      • 代理的个性-增强
      • 动态代理

代理模式

例子

public interface IGamePlayer {/*** 登录游戏*/void login(String user, String password);/*** 杀怪,网络游戏的主要特色*/void killBoss();/*** 升级*/void upgrade();
}
public class GamePlayer implements IGamePlayer {private String name = "";/*** 通过构造函数传递名称* @param _name*/public GamePlayer(String _name) {this.name = _name;}/*** 打怪,最期望的就是杀老怪*/@Overridepublic void killBoss() {System.out.println(this.name + "在打怪!");}/*** 进游戏之前你肯定要登录吧,这是一个必要条件* @param user* @param password*/@Overridepublic void login(String user, String password) {System.out.println("登录名为" + user + "的用户" + this.name + "登录成功!");}/*** 升级,升级有很多方法,花钱买是一种,做任务也是一种*/@Overridepublic void upgrade() {System.out.println(this.name + " 又升了一级!");}
}
public class GamePlayerProxy implements IGamePlayer {private IGamePlayer gamePlayer = null;/*** 通过构造函数传递要对谁进行代练** @param _gamePlayer*/public GamePlayerProxy(IGamePlayer _gamePlayer) {this.gamePlayer = _gamePlayer;}/*** 代练杀怪*/@Overridepublic void killBoss() {this.gamePlayer.killBoss();}/*** 代练登录** @param user* @param password*/@Overridepublic void login(String user, String password) {this.gamePlayer.login(user, password);}/*** 代练升级*/@Overridepublic void upgrade() {this.gamePlayer.upgrade();}
}
public class Client {/*    public static void main(String[] args) {//定义一个痴迷的玩家IGamePlayer player = new GamePlayer("张三");//开始打游戏,记下时间戳System.out.println("开始时间是:2009-8-25 10:45");player.login("zhangSan", "password");//开始杀怪player.killBoss();//升级player.upgrade();//记录结束游戏时间System.out.println("结束时间是:2009-8-26 03:40");}*/public static void main(String[] args) {//定义一个痴迷的玩家IGamePlayer player = new GamePlayer("张三");//然后再定义一个代练者IGamePlayer proxy = new GamePlayerProxy(player);//开始打游戏,记下时间戳System.out.println("开始时间是:2009-8-25 10:45");proxy.login("zhangSan", "password");//开始杀怪proxy.killBoss();//升级proxy.upgrade();//记录结束游戏时间System.out.println("结束时间是:2009-8-26 03:40");}
}

定义

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

  • Subject抽象主题角色
    抽象主题类可以是抽象类也可以是接口,是一个最普通的业务类型定义,无特殊要求。
public interface Subject {public void request();
}
  • RealSubject具体主题角色
    也叫做被委托角色、被代理角色。它才是冤大头,是业务逻辑的具体执行者。
public class RealSubject implements Subject {@Overridepublic void request() {System.out.println("real subject");}
}
  • Proxy代理主题角色
    也叫做委托类、代理类。它负责对真实角色的应用,把所有抽象主题类定义的方法限制 委托给真实主题角色实现,并且在真实主题角色处理完毕前后做预处理和善后处理工作。
public class Proxy implements Subject {/*** 要代理的是哪个实现类*/private Subject subject = null;/*** 默认被代理者*/public Proxy() {this.subject = new Proxy();}public Proxy(Object... objects) {}public Proxy(Subject subject) {this.subject = subject;}@Overridepublic void request() {this.before();this.subject.request();this.after();}private void before() {}private void after() {}
}

优点

  • 职责清晰
    真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的事务,通过后期的代理完成一件事务,附带的结果就是编程简洁清晰。
  • 高扩展性
    具体主题角色是随时都会发生变化的,只要它实现了接口,甭管它如何变化,都逃不脱如来佛的手掌(接口),那我们的代理类完全就可以在不做任何修改的情况下使用。
  • 智能化
    这在我们以上的讲解中还没有体现出来,不过在我们以下的动态代理章节中你就会看到 代理的智能化。

使用场景

打官司为什么要找个律师?因为你不想参与中间过程的是是非非,只要完成自己的答辩就成,其他的比如事前调查、事后追查都由律师来搞定,这就是为了减轻你的负担。代理模式的使用场景非常多,大家可以看看Spring AOP,这是一个非常典型的动态代理。

扩展

在网络上代理服务器设置分为透明代理和普通代理,我们设计模式中的普通代理和强制代理也是类似的一种结构,普通代理就是我们要 知道代理的存在,也就是类似的GamePlayerProxy这个类的存在,然后才能访问;强制代理则 是调用者直接调用真实角色,而不用关心代理是否存在,其代理的产生是由真实角色决定。

普通代理

普通代理,它的要求就是客户端只能访问代理角色,而不能访问真实角色,这是比较简单的。
修改了构造函数,传递进来一个代理者名称,即可进行代理,在这种改造下,系统 更加简洁了,调用者只知道代理存在就可以,不用知道代理了谁。
修改GamePlayer

public class GamePlayer implements IGamePlayer {private String name = "";/*** 构造函数限制谁能创建对象,并同时传递姓名** @param _gamePlayer* @param _name* @throws Exception*/public GamePlayer(IGamePlayer _gamePlayer, String _name) throws Exception {if (_gamePlayer == null) {throw new Exception("不能创建真实角色!");} else {this.name = _name;}}/*** 打怪,最期望的就是杀老怪*/@Overridepublic void killBoss() {System.out.println(this.name + "在打怪!");}/*** 进游戏之前你肯定要登录吧,这是一个必要条件** @param user* @param password*/@Overridepublic void login(String user, String password) {System.out.println("登录名为" + user + "的用户" + this.name + "登录成功!");}/*** 升级,升级有很多方法,花钱买是一种,做任务也是一种*/@Overridepublic void upgrade() {System.out.println(this.name + " 又升了一级!");}
}

修改代理类

public class GamePlayerProxy implements IGamePlayer {private IGamePlayer gamePlayer = null;public GamePlayerProxy(String  name) {try {gamePlayer = new GamePlayer(this,name);}catch (Exception e){// 异常处理}}/*** 代练杀怪*/@Overridepublic void killBoss() {this.gamePlayer.killBoss();}/*** 代练登录** @param user* @param password*/@Overridepublic void login(String user, String password) {this.gamePlayer.login(user, password);}/*** 代练升级*/@Overridepublic void upgrade() {this.gamePlayer.upgrade();}
}

修改场景类

public class Client {public static void main(String[] args) {//然后再定义一个代练者IGamePlayer proxy = new GamePlayerProxy("张三");//开始打游戏,记下时间戳System.out.println(String.format("开始时间是:%s", LocalDateTime.now()));proxy.login("zhangSan", "password");//开始杀怪proxy.killBoss();//升级proxy.upgrade();//记录结束游戏时间System.out.println("结束时间是:2009-8-26 03:40");}}

在该模式下,调用者只知代理而不用知道真实的角色是谁,屏蔽了真实角色的变更对高层模块的影响,真实的主题角色想怎么修改就怎么修改,对高层次的模 块没有任何的影响,只要你实现了接口所对应的方法,该模式非常适合对扩展性要求较高的 场合。当然,在实际的项目中,一般都是通过约定来禁止new一个真实的角色,这也是一个非常好的方案。
注意:普通代理模式的约束问题,尽量通过团队内的编程规范类约束,因为每一个主题 类是可被重用的和可维护的,使用技术约束的方式对系统维护是一种非常不利的因素。

强制代理


强制代理是你必须通过真实角色查找到代理角色,否则你不能访问。高层模块new 了一个真实角色的对象,返回的却是代理角色。

public interface IGamePlayer {/*** 登录游戏*/void login(String user, String password);/*** 杀怪,网络游戏的主要特色*/void killBoss();/*** 升级*/void upgrade();/*** 每个人都可以找一下自己的代理* @return*/IGamePlayer getProxy();
}
public class GamePlayer implements IGamePlayer {private String name = "";private IGamePlayer proxy = null;/*** @param name*/public GamePlayer(String name) {this.name = name;}/*** 找到自己的代理** @return*/@Overridepublic IGamePlayer getProxy() {this.proxy = new GamePlayerProxy(this);return this.proxy;}/*** 打怪,最期望的就是杀老怪*/@Overridepublic void killBoss() {if (this.isProxy()) {System.out.println(this.name + "在打怪!");}else {System.out.println("请使用指定的代理对象");}}/*** 进游戏之前你肯定要登录吧,这是一个必要条件** @param user* @param password*/@Overridepublic void login(String user, String password) {if(this.isProxy()){System.out.println("登录名为" + user + "的用户" + this.name + "登录成功!");}else{System.out.println("请使用指定的代理对象");}}/*** 升级,升级有很多方法,花钱买是一种,做任务也是一种*/@Overridepublic void upgrade() {if(this.isProxy()){System.out.println(this.name + " 又升了一级!");}else{System.out.println("请使用指定的代理对象");}}/*** 校验是否是代理访问* @return*/private boolean isProxy() {return this.proxy != null;}}
public class GamePlayerProxy implements IGamePlayer {private IGamePlayer gamePlayer = null;//构造函数传递用户名/*** 构造函数传递用户名* @param _gamePlayer*/public GamePlayerProxy(IGamePlayer _gamePlayer) {this.gamePlayer = _gamePlayer;}//代练杀怪@Overridepublic void killBoss() {this.gamePlayer.killBoss();}//代练登录@Overridepublic void login(String user, String password) {this.gamePlayer.login(user, password);}// 代练升级@Overridepublic void upgrade() {this.gamePlayer.upgrade();}//代理的代理暂时还没有,就是自己@Overridepublic IGamePlayer getProxy() {return this;}
}
public class Client {public static void main(String[] args) {//定义一个游戏的角色IGamePlayer player = new GamePlayer("张三");//获得指定的代理IGamePlayer proxy = player.getProxy();//开始打游戏,记下时间戳System.out.println("开始时间是:2009-8-25 10:45");proxy.login("zhangSan", "password");//开始杀怪proxy.killBoss();//升级proxy.upgrade();//记录结束游戏时间System.out.println("结束时间是:2009-8-26 03:40");}
}

代理的个性-增强


一个类可以实现多个接口,完成不同任务的整合。也就是说代理类不仅仅可以实现主题 接口,也可以实现其他接口完成不同的任务,而且代理的目的是在目标对象方法的基础上作增强,这种增强的本质通常就是对目标对象的方法进行拦截和过滤。例如游戏代理是需要收费的,升一级需要5元钱,这个计算功能就是代理类的个性,它应该在代理的接口中定义。
创建IProxy接口

public interface IProxy {/*** 计算费用*/void count();
}

代理类实现该接口

public class GamePlayerProxy implements IGamePlayer, IProxy {private IGamePlayer gamePlayer = null;//构造函数传递用户名/*** 构造函数传递用户名** @param _gamePlayer*/public GamePlayerProxy(IGamePlayer _gamePlayer) {this.gamePlayer = _gamePlayer;}/*** 代练杀怪*/@Overridepublic void killBoss() {this.gamePlayer.killBoss();}/*** 代练登录*/@Overridepublic void login(String user, String password) {this.gamePlayer.login(user, password);}/*** 代练升级*/@Overridepublic void upgrade() {this.gamePlayer.upgrade();this.count();}/*** 代理的代理暂时还没有,就是自己*/@Overridepublic IGamePlayer getProxy() {return this;}@Overridepublic void count() {System.out.println("升级总费用:150元");}
}

动态代理

动态代理是在实现阶段不用关心代理谁,而在运行阶段才指定代理哪一个对象。相对来说,自己写代理类的方式就是静态代理。

InvocationHandler是JDK提供的动态代理接口,对被代理类的方法进行代理。通过 InvocationHandler接口,所有方法都由该Handler来进行处理,即所有被代理的方法都由InvocationHandler接管实际的处理任务。

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*/@Overridepublic 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 Client {public static void main(String[] args) {//定义一个痴迷的玩家IGamePlayer player = new GamePlayer("张三");//定义一个handlerInvocationHandler handler = new GamePlayIH(player);//开始打游戏,记下时间戳System.out.println("开始时间是:2009-8-25 10:45");//获得类的class loaderClassLoader 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("结束时间是:2009-8-26 03:40");}
}

只要在代理中增加一个判断就可以决定是否要发送信息。AOP编程没有使用什么新的技术,但是它对我们的设计、编码有非常大的影响,对于日志、事务、权限等都可以在系统设计阶段不用考虑,而在设计后通 过AOP的方式切过去。


很简单,两条独立发展的线路。动态代理实现代理的职责,业务逻辑Subject实现相关的 逻辑功能,两者之间没有必然的相互耦合的关系。通知Advice从另一个切面切入,最终在高层模块也就是Client进行耦合,完成逻辑的封装任务。

public interface Subject {void doSomething(String str);
}
public class RealSubject implements Subject {@Overridepublic void doSomething(String str) {System.out.println("do something!--->"+str);}
}
public class MyInvocationHandler implements InvocationHandler {/*** 被代理的对象*/private Object target = null;/*** 通过构造函数传递一个对象* @param _obj*/public MyInvocationHandler(Object _obj) {this.target = _obj;}/*** 代理方法* @param proxy* @param method* @param args* @return* @throws Throwable*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//执行被代理的方法return method.invoke(this.target, args);}
}
public interface IAdvice {void exec();
}
public class DynamicProxy<T> {public static <T> T newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) {//寻找JoinPoint连接点,AOP框架使用元数据定义if (true) {(new BeforeAdvice()).exec();}//执行一个前置通知 (new BeforeAdvice()).exec(); }// 执行目标,并返回结果return (T) Proxy.newProxyInstance(loader, interfaces, h);}
}
public class BeforeAdvice implements IAdvice{@Overridepublic void exec() {System.out.println("我是前置通知,我被执行了");}
}
public class Client {public static void main(String[] args) {Subject subject = new RealSubject();InvocationHandler handler = new MyInvocationHandler(subject);Subject proxy = DynamicProxy.newProxyInstance(subject.getClass().getClassLoader(),subject.getClass().getInterfaces(),handler);proxy.doSomething("Finish");}
}

在DynamicProxy类 中,我们有这样的方法:this.obj=Proxy.newProxyInstance(c.getClassLoader(),c.getInterfaces(),new MyInvocationHandler(_obj));
该方法是重新生成了一个对象,为什么要重新生成?你要使用代理呀,注意 c.getInterfaces()这句话,这是非常有意思的一句话,是说查找到该类的所有接口,然后实现接口的所有方法。当然了,方法都是空的,由谁具体负责接管呢?是new MyInvocationHandler(_Obj)这个对象。于是我们知道一个类的动态代理类是这样的一个类, 由InvocationHandler的实现类实现所有的方法,由其invoke方法接管所有方法的实现,其动态调用过程如图12-9所示。

动态代理还有扩展空间,DynamicProxy类,它是一个通用类,不具有业务意义,如果我们再产生一个实现类就很有意义了。

public class SubjectDynamicProxy extends DynamicProxy {public static <T> T newProxyInstance(Subject subject) {//获得ClassLoader ClassLoader loader = subject.getClass().getClassLoader();//获得接口数组 Class<?>[] classes = subject.getClass().getInterfaces();//获得handler IInvocationHandler handler = new MyInvocationHandler(subject);return newProxyInstance(loader, classes, handler);}
}
public class Client {public static void main(String[] args) {//定义一个主题Subject subject = new RealSubject();//定义主题的代理Subject proxy = SubjectDynamicProxy.newProxyInstance(subject);//代理的行为proxy.doSomething("Finish");}
}

设计模式之禅-代理模式相关推荐

  1. 设计模式学习之代理模式学习(一)

    设计模式学习之代理模式学习(一) 关于设计模式想必学习过Java语言的人都知道吧,当时对其进行深入学习的的人应该不是很多.在我看来设计方面的知识相比于框架应用配置等知识要有意思的多,并且设计模式的对一 ...

  2. 设计模式之静态代理模式实战

    转载自 设计模式之静态代理模式实战 静态代理模式很简单,代理类和实现类都实现相同的接口,然后通过代理类来调用实现类的方法. 如我们想保存用户信息之前打印用户信息,或者保存用户信息之后把这些信息缓存下来 ...

  3. 设计模式之蝉——代理模式上中

    代理模式的扩展 1 普通代理 :这种代理就是客户端只能访问代理角色,而不能访问真实角色.与设计模式之蝉--代理模式上 片基本差不多. (1)Subject抽象主题角色: (2)RealSubject具 ...

  4. 每日一个设计模式之【代理模式】

    文章目录 每日一个设计模式之[代理模式] ☁️前言

  5. 设计模式:4.代理模式

    代理模式 代理模式属于结构型模式 定义:为其他对象提供一种代理以控制对这个对象的访问 代理模式就是在操作原对象的时候,多出来一个代理类,用来对原对象的访问进行控制和替代原对象进行一些操作 模式类图 角 ...

  6. 大话设计模式C++版——代理模式

        本篇開始前先发个福利,程杰的<大话设计模式>一书高清电子版(带文件夹)已上传至CSDN,免积分下载. 下载地址:http://download.csdn.net/detail/gu ...

  7. Java24种设计模式(第二种)--代理模式(Proxy Pattern)

    Java24种设计模式 (第二种) 一.代理模式(Proxy Pattern) 模式逻辑: 什么是代理模式呢?我很忙,忙的没空理你,那你要找我呢就先找我的代理人吧,那代理人总要知道 被代理人能做哪些事 ...

  8. 23种java设计模式详解-代理模式

    什么是代理模式: Proxy模式又叫做代理模式,是构造型的设计模式之一,它可以为其他对象提供一种代理(Proxy)以控制对这个对象的访问.所谓代理,是指具有与代理元(被代理的对象)具有相同的接口的类, ...

  9. 《设计模式系列》- 代理模式

    有情怀,有干货,微信搜索[三太子敖丙]关注这个有一点点东西的程序员. 本文 GitHub https://github.com/JavaFamily 已收录,有一线大厂面试完整考点.资料以及我的系列文 ...

最新文章

  1. 聊聊jump consistent hash
  2. 数据结构——栈——2016_11_21
  3. 二、Windows基础数据类型
  4. 为什么使用hibernate
  5. CentOS下开启配置端口转发
  6. 安全测试===sqlmap(零)转载
  7. php的floorval函数,php 取整函数(floor,ceil,round,intval)_PHP教程
  8. Nginx学习---Nginx的详解_【all】
  9. QT之创建计算器(五)
  10. Flutter之播放视频
  11. HTML5七夕情人节表白网页制作【粉色樱花雨3D相册】HTML+CSS+JavaScript
  12. NSGA-II算法介绍
  13. Ubuntu14.04 64位安装iNode客户端
  14. STM32——时钟、HSE、旁路模式、有源晶振
  15. 为什么宁愿工资低点,也不建议去外包公司?
  16. A Structure from Motion System
  17. tar/zip 压缩解压
  18. not annotated with HTTP method type (ex. GET, POST) 问题解决
  19. 【深度学习kears+tensorflow】电影评论分类:二分类问题
  20. 【雷达与对抗】【2017.06】空中目标的无源雷达探测

热门文章

  1. dd写入虚拟软盘第一扇区而不截断
  2. windows cmd使用attrib隐藏文件的方法
  3. Delphi线程内部
  4. 《穷爸爸和富爸爸》《思考致富》读后感
  5. 算力引领 数“聚”韶关——第二届中国韶关大数据创新创业大赛圆满收官
  6. 奇酷软件测试,我们是如何测试360手机浏览器的 --360手机浏览器测试范围概述
  7. 东方朔《诫子书》- 诸葛亮《诫子书》
  8. linux查看图像大小_如何在Linux上调整一批图像的大小?
  9. 深圳建广数字科技有限公司(青岛)顶岗实习总结
  10. el-table-column根据条件修改字体颜色,el-table-column序号展示