设计模式之禅-代理模式
目录
- 代理模式
- 例子
- 定义
- 优点
- 使用场景
- 扩展
- 普通代理
- 强制代理
- 代理的个性-增强
- 动态代理
代理模式
例子
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");}
}
设计模式之禅-代理模式相关推荐
- 设计模式学习之代理模式学习(一)
设计模式学习之代理模式学习(一) 关于设计模式想必学习过Java语言的人都知道吧,当时对其进行深入学习的的人应该不是很多.在我看来设计方面的知识相比于框架应用配置等知识要有意思的多,并且设计模式的对一 ...
- 设计模式之静态代理模式实战
转载自 设计模式之静态代理模式实战 静态代理模式很简单,代理类和实现类都实现相同的接口,然后通过代理类来调用实现类的方法. 如我们想保存用户信息之前打印用户信息,或者保存用户信息之后把这些信息缓存下来 ...
- 设计模式之蝉——代理模式上中
代理模式的扩展 1 普通代理 :这种代理就是客户端只能访问代理角色,而不能访问真实角色.与设计模式之蝉--代理模式上 片基本差不多. (1)Subject抽象主题角色: (2)RealSubject具 ...
- 每日一个设计模式之【代理模式】
文章目录 每日一个设计模式之[代理模式] ☁️前言
- 设计模式:4.代理模式
代理模式 代理模式属于结构型模式 定义:为其他对象提供一种代理以控制对这个对象的访问 代理模式就是在操作原对象的时候,多出来一个代理类,用来对原对象的访问进行控制和替代原对象进行一些操作 模式类图 角 ...
- 大话设计模式C++版——代理模式
本篇開始前先发个福利,程杰的<大话设计模式>一书高清电子版(带文件夹)已上传至CSDN,免积分下载. 下载地址:http://download.csdn.net/detail/gu ...
- Java24种设计模式(第二种)--代理模式(Proxy Pattern)
Java24种设计模式 (第二种) 一.代理模式(Proxy Pattern) 模式逻辑: 什么是代理模式呢?我很忙,忙的没空理你,那你要找我呢就先找我的代理人吧,那代理人总要知道 被代理人能做哪些事 ...
- 23种java设计模式详解-代理模式
什么是代理模式: Proxy模式又叫做代理模式,是构造型的设计模式之一,它可以为其他对象提供一种代理(Proxy)以控制对这个对象的访问.所谓代理,是指具有与代理元(被代理的对象)具有相同的接口的类, ...
- 《设计模式系列》- 代理模式
有情怀,有干货,微信搜索[三太子敖丙]关注这个有一点点东西的程序员. 本文 GitHub https://github.com/JavaFamily 已收录,有一线大厂面试完整考点.资料以及我的系列文 ...
最新文章
- 聊聊jump consistent hash
- 数据结构——栈——2016_11_21
- 二、Windows基础数据类型
- 为什么使用hibernate
- CentOS下开启配置端口转发
- 安全测试===sqlmap(零)转载
- php的floorval函数,php 取整函数(floor,ceil,round,intval)_PHP教程
- Nginx学习---Nginx的详解_【all】
- QT之创建计算器(五)
- Flutter之播放视频
- HTML5七夕情人节表白网页制作【粉色樱花雨3D相册】HTML+CSS+JavaScript
- NSGA-II算法介绍
- Ubuntu14.04 64位安装iNode客户端
- STM32——时钟、HSE、旁路模式、有源晶振
- 为什么宁愿工资低点,也不建议去外包公司?
- A Structure from Motion System
- tar/zip 压缩解压
- not annotated with HTTP method type (ex. GET, POST) 问题解决
- 【深度学习kears+tensorflow】电影评论分类:二分类问题
- 【雷达与对抗】【2017.06】空中目标的无源雷达探测
热门文章
- dd写入虚拟软盘第一扇区而不截断
- windows cmd使用attrib隐藏文件的方法
- Delphi线程内部
- 《穷爸爸和富爸爸》《思考致富》读后感
- 算力引领 数“聚”韶关——第二届中国韶关大数据创新创业大赛圆满收官
- 奇酷软件测试,我们是如何测试360手机浏览器的 --360手机浏览器测试范围概述
- 东方朔《诫子书》- 诸葛亮《诫子书》
- linux查看图像大小_如何在Linux上调整一批图像的大小?
- 深圳建广数字科技有限公司(青岛)顶岗实习总结
- el-table-column根据条件修改字体颜色,el-table-column序号展示