Head First 设计模式 —— 13. 代理 (Proxy) 模式
思考题
如何设计一个支持远程方法调用的系统?你要怎样才能让开发人员不用写太多代码?让远程调用看起来像本地调用一样,毫无瑕疵? P435
- 已经接触过 RPC 了,所以就很容易知道具体流程:客户端调用目标类的代理对象(消费者)的方法,消费者内部将相关调用信息通过网络传到服务端对应的目标类的代理对象(生产者)中,生产者解析调用信息,然后真正去调用目标类的实际对象,并将返回结果回传给消费者,消费者再返回给客户端。 RPC 框架使用代理模式使得内部一系列处理及信息传输等对客户端和服务端是透明的,客户端会认为实际是本地调用一样,不知道调用了远程方法;服务端也不知道是在给远程对象提供服务。
思考题
远程调用程序应该完全透明吗?这是个好主意吗?这个方法可能会产生什么问题? P435
- 远程调用程序不应该完全透明。由于引入了网络通信和数据处理(序列化、反序列化和压缩等),可能在相关过程会异常,客户端应该知晓并处理这些异常,而不应该让 RPC 框架消化掉这些异常而返回一些默认值。
代理模式
为另一个对象提供一个替身或占位符以控制对这个对象的访问。 P460
特点
- 代理控制访问
- 远程代理:控制访问远程对象
P460
- 虚拟代理:控制访问创建开销大的资源
P460
- 保护代理:基于权限控制对资源的访问
P460
- 动态代理:在运行时动态地创建一个代理类,并将方法的调用转发到指定的类
P474
- 防火墙代理:控制网络资源的访问,保护主题免于“坏客户”的侵害。多用于防火墙系统
P488
- 智能引用代理:当主题被引用时,进行额外的动作,例如计一个对象被引用的次数。可用于对某些操作的日志记录
P488
- 缓存代理:为开销大的运算结果提供暂时存储;也允许多个客户共享结果,以减少计算或网络延迟。多用于 Web 服务器代理,以及内容管理与出版系统
P488
- 同步代理:在多线程的情况下为主题提供安全的访问。可用于 JavaSpaces ,为分散式环境内的潜在对象集合提供同步访问控制
P489
- 复杂隐藏代理:用来隐藏一个类的复杂集合的复杂度,并进行访问控制;有时候也成为外观代理,但与外观模式不同,因为代理控制访问,而外观模式只提供另一组接口
P489
- 写入时复制代理:用来控制对象的复制,方法是延迟对象的复制,直到客户真的需要为止。这是虚拟代理的变体。
CopyOnWriteArrayList
使用这种方式P489
- 远程代理:控制访问远程对象
缺点
- 代理会造成设计中类的数目增加
P491
代理模式和装饰器模式 P471
- 代理模式控制对象的访问
- 装饰器模式为对象增加行为
代理模式和适配器模式的区别 P471
- 代理模式实现相同的接口(保护代理可能只提供给客户部分接口,与某些适配器很像)
- 适配器模式改变对象适配的接口
思考题
class ImageProxy implements Icon {// 实例变量构造器在这里public int getIconWidth() {if (imageIcon != null) {return imageIcon.getIconWidth();} else {return 800;}}public int getIconHeight() {if (imageIcon != null) {return imageIcon.getIconHeight();} else {return 600;}}public void paintIcon(final Component c, Graphics g, int y, int y) {if (imageIcon != null) {imageIcon.paintIcon(c, g, x, y);} else {g.drawString("Loading CD cover, please wait...", x + 300, y + 190);// 实例化 imageIcon 获取图片}}
}
以上为 CD 封面虚拟代理, ImageProxy
类似乎有两个,由条件语句控制的状态。你能否用另一个设计模式清理这样的代码?你要如何重新设计 ImageProxy
? P468
- 可以用状态模式清理掉条件语句。设置两个状态
ImageNotLoaded
和ImageLoaded
,分别将各个方法内条件语句内的代码放入这个两个状态的对应方法中,初始状态是ImageNotLoaded
,当第一次调用paintIcon
方法时,开始实例化imageIcon
获取图片,成功后设置状态为ImageLoaded
。
思考题
NonOwnerInvocationHandler
工作的方式除了它允许调用 setHotOrNotRating()
和不允许调用其他 set 方法之外,与 NonOwnerInvocationHandler
是很相似的。请写出 NonOwnerInvocationHandler
的代码: P482
import java.lang.reflect.*;public class NonOwnerInvocationHandler implements InvocationHandler {PersonBean person;public NonOwnerInvocationHandler(PersonBean person) {this.person = person;}public Object invoke(Object proxy, Method method, Object[] args) throws IllegalAccessException {try {String methodName = method.getName();if (methodName.startsWith("get")) {return method.invoke(person, args);} else if(methodName.equals("setHotOrNotRating")) {return method.invoke(person, args);} else if(methodName.startsWith("set")) {throw new IllegalAccessException();}} catch (IllegalAccessException e) {e.printStackTrace();}return null;}
}
思考题
创建动态代理所需要的代码很短,请你写下 getNonOwnerProxy()
,该方法会返回 NonOwnerInvocationHandler
的代理。更进一步,请写下 getProxy()
方法,参数是 handler
和 person
,返回值是使用此 handler
的代理。 P483
PersonBean getNonOwnerProxy(PersonBean person) {return (PersonBean) Proxy.newProxyInstance(person.getClass().getClassLoader(), person.getClass().getInterfaces(), new NonOwnerInvocationHandler(person));
}PersonBean getProxy(InvocationHandler handler, PersonBean person) {return (PersonBean) Proxy.newProxyInstance(person.getClass().getClassLoader(), person.getClass().getInterfaces(), handler);
}
思考题
如何知道某个类是不是代理类? P486
- JDK 动态代理的类是
Proxy
的子类,有一个静态方法isProxyClass()
,此方法的返回值如果为true
,表示这是一个动态代理类。 - 【存疑】 代理类还会实现特定的某些接口
- 在 Java8 中调用
proxy.getClass().getInterfaces()
及其他与获取接口有关的方法,并未发现实现新接口
- 在 Java8 中调用
思考题
能传入 newProxyInstance()
的接口类型,有没有什么限制?
- 传入的接口数组内只能有接口,不能有类
P486
- 如果接口不是
public
,就必须属于同一个package
P486
- 【存疑】 不同的接口内,不可以有名称和参数完全一样的方法
P486
- 经过 Java8 中实践确认没有此限制,不过永远只会识别为其中一个接口(接口数组内第一次出现该方法的接口)的方法
- 接口数组内的接口可以不是被代理类实现的接口,代理类实现了接口数组内的所有接口,所有接口的调用都可以被拦截处理
- 被代理类可以不实现任何接口,自己指定接口和相关调用处理逻辑也能使用
思考题
配对下列模式和描述: P487
代理模式:包装另一个对象,并控制对它的访问
外观模式:包装许多对象以简化它们的接口
装饰器模式:包装另一个对象,并提供额外的行为
适配器模式:包装另一个对象,并提供不同的接口
所思所想
- 以前也看过不同动态代理的实现,但只是走马观花式地看一遍如何实现,没有实际去动手,这次读书时实际动手后感觉理解更深入了一点,大概更能了解内部是如何流转的
- 实践是检验真理的唯一标准。书中存在部分说明可能错误或者不适用于当前版本,要利用好身边的工具,多实践操作,不能尽信书
本文首发于公众号:满赋诸机(点击查看原文) 开源在 GitHub :reading-notes/head-first-design-patterns
Head First 设计模式 —— 13. 代理 (Proxy) 模式相关推荐
- 当Android遇上设计模式之代理(Proxy)模式
文章目录 1. 代理模式 1.1 代码实现 1.2 使用场景 2. 静态代理与动态代理 设计模式六大原则: 单一职责原则:就一个类仅有一个引起它变化的原因,即类承担的职责单一性: 开放封闭原则:类.模 ...
- 设计模式学习笔记——代理(Proxy)模式
设计模式学习笔记--代理(Proxy)模式 @(设计模式)[设计模式, 代理模式, proxy] 设计模式学习笔记代理Proxy模式 基本介绍 代理案例 类图 实现代码 Printable接口 Pri ...
- Java二十三种设计模式 之代理(proxy)
Java二十三种设计模式 之代理(proxy) 今天我们学习一下静态代理和动态代理 我们来看代码(写一个坦克运行了多少时间): 第一种方法: public calss Tank implements ...
- 设计模式---代理(Proxy)模式
1 定义 代理模式,为想要访问的对象创建一个代理,使访问原对象变为访问代理对象.代理模式可以提供非常好的访问控制.生活中最多的代理模式例子就是中介. 2 代理模式结构与实现 代理模式通用类图如下所示( ...
- 代理(Proxy)模式 vs. 代理(Agency)机构
有一天,我将设计模式中的代理模式和我们社会中的代理机构联系起来.发现他们之间有非常多的相似的地方,而且可以互相借鉴. 在讨论这个话题之前,我的老板就率先指出了我的缺点.代理是Proxy不是Agency ...
- C#设计模式(13)——代理模式(Proxy Pattern)
一.引言 在软件开发过程中,有些对象有时候会由于网络或其他的障碍,以至于不能够或者不能直接访问到这些对象,如果直接访问对象给系统带来不必要的复杂性,这时候可以在客户端和目标对象之间增加一层中间层,让代 ...
- 设计模式--代理(Proxy)模式
模式定义 为其他对象提供一种代理以控制(隔离,使用接口)对这个对象的访问 类图 要点总结 "增加一层间接层"是软件系统中对许多复杂问题的一种常见解决方法,在面向对象系统中,直接使用 ...
- 《研磨设计模式》chap11 代理proxy模式
场景:需要查询所有用户数据,不能翻页都显示出来,只显示姓名,在需要的情况下,点击某个用户的姓名出现详细信息. 1. 正常编码:仅查询当前数据 public static void main(Strin ...
- Java 代理(proxy)模式
代理模式(Proxy Pattern) 代理模式是常用的Java 设计模式,它的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等.代 ...
最新文章
- Delphi调用java开发的WebService,传入参数出错
- java点滴(6)之java引用
- (数据结构)前缀,后缀以及中缀表达式
- 前端只是切图仔?来学学给开发人看的UI设计
- 云存储精华问答 | 云存储的优势在哪?
- 真香!腾讯再发阳光普照奖:每人100股,价值超6万!
- bzoj2595 [Wc2008]游览计划
- 使用密码字典暴力破解加密rar、zip压缩文件
- 中国主要水系、河流、湖泊数据矢量数据(shp格式)下载地址
- Lab4: traps
- 加密软件不能安装软件
- 根据开始日期,结束日期,计算出日历上有几周的方法
- Tensorflow1.7+cuda9.0+cudnn7.0中的各种意(da)外(keng)
- Chrome 下IndexedDB 本地存储
- js 数组的every() 方法
- mysql无法执行二进制文件_kail系统64,mysql64,出现-bash: bin/mysqld: 无法执行二进制文...
- 如何写IT项目解决方案
- 4.导出UnityPackage(AssetDatabase.ExportPackage(assetPathName ,fileName ,ExportPackageOptions.Recurse )
- 全国计算机osta 中级,OSTA中级解题步骤答案.doc
- 【ElementUI】el-table 设置 max-height 后,当表格数据未超过高度时,表格右侧会出现空白列的解决方案