写在前面:

代理模式的内部原理,作用及远程代理的实现在上一篇博文中都做了详细解释,本文只是对其内容的补充,介绍其它代理

一.虚拟代理

首先,明确虚拟代理的作用:在巨大对象被真正创建出来之前,用虚拟代理来代替巨大对象(保证了巨大对象在需要的时候才被创建,避免资源浪费)

虚拟代理我们可能经常在用,只是不认识它而已,比如:

我们的程序中可能存在密集的计算或者从服务器取得大量数据这样耗时耗资源的操作,一般都会先显示默认信息,或者显示动画表示我们的程序还在努力工作,在操作完成之后显示获得的信息

其中就用到了虚拟代理的思想,只是虚拟代理把等待操作完成期间的工作全部分离出来封装到了一个虚拟代理对象里面,与客户直接交互的是虚拟代理而不是实际做事情的对象

虚拟代理接到客户请求后做了这样几件事情:

  1. 如果实际对象不存在(尚未创建或者尚未创建完成)则记录客户请求,显示默认信息,等到实际对象创建完成之后把请求传递给实际对象
  2. 如果实际对象已经存在,则直接把客户请求委托给实际对象
  3. 在实际对象没有传回操作结果之前,代理对象负责显示默认信息(或者做默认处理)
  4. 得到操作结果后直接传递给客户(没错,代理的任务就是“交”与“接”)

既然是代理,那么就要伪装成实际对象,不能让客户发觉代理的存在,所以虚拟代理的类图是这样的:

Client持有一个虚拟代理对象Proxy,Proxy持有一个具体服务对象MyService

而更重要的是:虚拟代理Proxy与具体服务MyService都扩展自Service接口,所以代理与实际对象具有相同的行为,客户不会发觉Proxy的存在

二.保护代理

保护代理扮演着经纪人的角色(不是所有人都可以联系我背后的明星,你们只能联系我,在我确定你不是坏人之后,才给你转接。。)

换句话说,保护代理为实际对象提供了访问控制,保证了只有合法的调用者才能得到服务

实现的话,类图和上面的虚拟代理完全一样,只是Proxy多了一项判断调用者权限的职责(发现什么了吗?我们好像可以把多个代理结合起来使用,当然这样的代理不再满足类的单一责任原则,不过没关系,我们完全可以创建一个代理的代理来管理一系列代理。。)

其实Java的API提供了对代理模式的支持(动态代理)

Java提供的动态代理的动态体现在Proxy代理类(没错,是类,而不是对象)是在运行时才被创建的,我们不能直接修改Proxy的内部实现,但我们可以通过InvocationHandler来告诉Proxy应该怎么做

采用动态代理来实现保护代理的基本思路是:创建多个代理,不同的代理表示不同权限的调用者对应的服务

那么让我们来设计一个情景,尝试用动态代理实现保护代理:

博文点赞机制:可以赞别人的博文,不能赞自己的博文,但博主和游客都可以看到被赞数目

情景比较简单,不过足够说明问题了,下面开始模拟实现

-------

首先要有BlogUser:

package ProxyPattern.ProtectionProxy;

/**
* 定义博客用户接口
* @author ayqy
*/
public interface BlogUser {
public void support();
public int getSupportCount();
}

还要有ConcreteUser,其实现过程比较简单,不在此展开

下面开始创建动态代理,这里有两种权限,博主与游客,所以我们至少需要两个不同的动态代理

AuthorHandler:

package ProxyPattern.ProtectionProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; /**
* 实现为作者服务的InvocationHandler
* @author ayqy
*/
public class AuthorHandler implements InvocationHandler{
BlogUser user; public AuthorHandler(BlogUser user){
this.user = user;
} @Override
public Object invoke(Object proxy, Method method, Object[] args)
throws IllegalAccessException {//声明非法访问异常
try {
/*如果博主想要查看被赞数目,则委托给调用者user实现具体操作*/
if(method.getName().equals("getSupportCount")){
return method.invoke(user, args);
}
/*如果博主要给自己点赞,则抛出异常表示拒绝*/
if(method.getName().equals("support")){
throw new IllegalAccessException();
}
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} return null;//如果调用其它方法,直接无视
} }

与此类似,还需要实现VisitorHandler

有了多个代理,我们还需要实现代理的代理,也就是保护代理(视调用者权限返回一个对应的动态代理)

ProtectionProxy:

package ProxyPattern.ProtectionProxy;

import java.lang.reflect.Proxy;

/**
* 实现保护代理
* @author ayqy
*/
public class ProtectionProxy {
/**
* @param user 调用者
* @return 作者对应的代理
*/
public BlogUser getAuthorProxy(BlogUser user){
return (BlogUser)Proxy.newProxyInstance(user.getClass().getClassLoader()
, user.getClass().getInterfaces()
, new AuthorHandler(user));
} /**
* @param user 调用者
* @return 游客对应的代理
*/
public BlogUser getVisitorProxy(BlogUser user){
return (BlogUser)Proxy.newProxyInstance(user.getClass().getClassLoader()
, user.getClass().getInterfaces()
, new VisitorHandler(user));
} /*在此处插入生成其它代理的方法*/
}

有了这些就足够了,做一个简单的测试:

package ProxyPattern.ProtectionProxy;

/**
* 实现测试类
* @author ayqy
*/
public class Test {
public static void main(String[] args){
//创建保护代理
ProtectionProxy proxy = new ProtectionProxy(); System.out.println("博主部分:");
//创建博主对应的代理
BlogUser authorProxy = proxy.getAuthorProxy(new ConcreteUser());
System.out.println("博主要查看被赞总数");
System.out.println("被赞 " + authorProxy.getSupportCount() + " 次");
//博主要给自己点赞
System.out.println("博主要给自己点赞");
try{
authorProxy.support();
}catch(Exception e){
System.out.println("点赞失败,无法给自己点赞");
}
System.out.println("自赞之后,被赞 " + authorProxy.getSupportCount() + " 次"); System.out.println("\n游客部分:");
//创建游客对应的代理
BlogUser vistorProxy = proxy.getVisitorProxy(new ConcreteUser());
System.out.println("游客要查看被赞总数");
System.out.println("被赞 " + vistorProxy.getSupportCount() + " 次");
//博主要给自己点赞
System.out.println("游客要给博主点赞");
try{
vistorProxy.support();
}catch(Exception e){
System.out.println("点赞失败,原因未知");
}
System.out.println("点赞之后,被赞 " + vistorProxy.getSupportCount() + " 次");
}
}

测试结果:

三.其它代理

代理模式应用很广泛,无法一一列举所有代理,在此简单介绍几种常见的代理:

代理名称 作用
防火墙代理 控制网络资源的访问,保护主题免于“坏客户”的侵害
智能引用代理 当主题被引用时,进行额外的动作,比如计算一个对象被引用的次数
缓存代理 为开销大的运算结果提供暂时存储,也允许多个客户共享结果,以减少计算或网络延迟
同步代理 在多线程的情况下为主题提供安全的访问
复杂隐藏代理(外观代理) 用来隐藏一个类的复杂集合的复杂度,并进行访问控制(比外观模式功能更强大)
写入时复制代理 用来控制对象的复制,方法是延迟对象的复制,直到真正需要复制为止(与虚拟代理类似)

当然,只要理解了最原始的远程代理,遇到这些代理就都很容易理解。。

设计模式之代理模式(Proxy Pattern)_补充篇的更多相关文章

  1. 乐在其中设计模式(C#) - 代理模式(Proxy Pattern)

    原文:乐在其中设计模式(C#) - 代理模式(Proxy Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 代理模式(Proxy Pattern) 作者:webabcd 介绍 为 ...

  2. 二十四种设计模式:代理模式(Proxy Pattern)

    代理模式(Proxy Pattern) 介绍为其他对象提供一个代理以控制对这个对象的访问. 示例有一个Message实体类,某对象对它的操作有Insert()和Get()方法,用一个代理来控制对这个对 ...

  3. c#设计模式之代理模式(Proxy Pattern)

    引言 代理这个词语,大家在现实世界已经频繁的接触过,例如火车站代理售票点,因为这些代理售票点的存在,我们不必要去火车站的售票处就可以查询或者取到火车票.代理点本身是没有能力生产车票的,我们在代理处享受 ...

  4. 乐在其中设计模式(C#) - 代理模式(Proxy Pattern)【转】

    介绍 为其他对象提供一个代理以控制对这个对象的访问. 示例 有一个Message实体类,某对象对它的操作有Insert()和Get()方法,用一个代理来控制对这个对象的访问. MessageModel ...

  5. 设计模式系列之代理模式(Proxy Pattern)——对象的间接访问

    说明:设计模式系列文章是读刘伟所著<设计模式的艺术之道(软件开发人员内功修炼之道)>一书的阅读笔记.个人感觉这本书讲的不错,有兴趣推荐读一读.详细内容也可以看看此书作者的博客https:/ ...

  6. 设计模式 - 代理模式(proxy pattern) 未使用代理模式 具体解释

    代理模式(proxy pattern) 未使用代理模式 详细解释 本文地址: http://blog.csdn.net/caroline_wendy 部分代码參考: http://blog.csdn. ...

  7. 代理模式(Proxy pattern)

    代理模式(proxy pattern):作用:为其他对象提供一种代理,以控制对这个对象的访问.代理对象在客户端对象和目标对象之间起中介的作用. 代理模式涉及到的角色: 抽象角色:声明真实对象和代理对象 ...

  8. 设计模式——代理模式(Proxy Pattern)

    代理模式(Proxy),为其他对象提供一种代理以控制对这个对象的访问. UML图: 模型设计: Subject类: package com.cnblog.clarck; /** * Subject 类 ...

  9. 大熊君说说JS与设计模式之------代理模式Proxy

    一,总体概要 1,笔者浅谈 当我们浏览网页时,网页中的图片有时不会立即展示出来,这就是通过虚拟代理来替代了真实的图片,而代理存储了真实图片的路径和尺寸,这就是代理方式的一种. 代理模式是比较有用途的一 ...

  10. 13.代理模式(Proxy Pattern)

    using System; namespace Test { //抽象角色:声明真实对象和代理对象的共同接口. //代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象, //同时代理 ...

随机推荐

  1. 让IIS支持PHP的配置步骤

    本文转自:http://marsren.blog.51cto.com/116511/41199/ 在Windows Server 2003的IIS6下配置ISAPI方式的PHP,配置方法是,在IIS的 ...

  2. np.random.random()系列函数

    1.np.random.random()函数参数 np.random.random((1000, 20)) 上面这个就代表生成1000行 20列的浮点数,浮点数都是从0-1中随机. 2.numpy.r ...

  3. 浅谈QT打印功能实现

    QT作为一款轻量级的集成开发环境,其设计的目标是使开发人员利用QT这个应用程序框架更加快速及轻易的开发应用程序.要达到此目的,要求QT必须能够跨平台,QT能够在32位及64位的Linux,MAC OS ...

  4. 14- Servlet.service() for servlet [mvc-dispatcher] in context with path [/collegeservice] threw exception [Request processing failed; nested exception is java.lang.NullPointerException] with root caus

    有的service没有依赖注入:

  5. 如何利用jQuery post传递含特殊字符的数据【转】

    在jQuery中,我们通常利用$.ajax或$.post进行数据传递处理,但这里通常不能传递特殊字符,如:“<”.本文就介绍如何传递这种含特殊字符的数据. 1.准备页面和控制端代码 页面代码如下 ...

  6. 大乐透 Java随机码

    package suijishu; import java.util.Random; // TODO Auto-generated method stub public class Xuanqi { ...

  7. PLSQL 块demo

    DECLARE   v_servid NUMBER(16);   v_stdno VARCHAR2(30); BEGIN   FOR i IN (SELECT rownum rn, t.*       ...

  8. PHP 用 ZipArchive 打包指定文件到zip供用户下载

    Ubuntu需安装zlib sudo apt-get install ruby sudo apt-get install zlib1g zlib1g.dev   Windows需开启php_zip.d ...

  9. java中配置JPA方法

    JPA全称Java Persistence API.JPA通过JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中 使用JPA进行保存对象时,可以用对象来接收,例 ...

  10. 2018.10.18 NOIP训练 [SCOI2018]Pipi 酱的日常(线段树)

    传送门 线段树好题啊. 题目要求的是sum−a−b−c+maxsum-a-b-c+maxsum−a−b−c+max{∣a+v∣+∣b+v∣+∣c+v∣|a+v|+|b+v|+|c+v|∣a+v∣+∣b ...