小more设计模式———代理模式

格式正确版本——请访问 http://note.youdao.com/noteshare?id=85dcd9dbb05c12ef3722b49e99afe9a6

1、场景
小more工作有大概半年了,由于小more学习能力不错,领导很看好小more,有意培养小more去带领团队,这不,今天小more和客户谈需求的时候,遇到这么一个问题。问题是这样的:
在一个HR人力项目中,客户提出:当选择一个部门或者分公司的时候,要把这个部门或者分公司的所有员工都显示出来,而且不要翻页,方便它们进行业务处理,在显示全部员工的时候,只需要显示名称就可以。但是也需要提供如下功能,在必要的时候可以选择查看某一个员工的详细信息。
问题分析:客户方式一个集团公司,有些部门或者分公司可能有好几百人,不让翻页。也就是一次性的获取这么多条数据并且展示出来,怎么实现呢?我们来看看传统的一般大家都能想到的思路:
不就是要获取某个部门下的所有员工信息嘛,直接使用SQL搞定,示意SQL如下
String sql = “select * from 用户表,部门表 where 用户表.deptId = 部门表.deptId and 部门表.deptId like "用户查看的deptId"”
如果按照这个实现来做,功能可能没问题,但是蕴含一个较大的问题。那就是,当一次性访问的数据条数过多,而且每条描述的数据量又很大的话,将会消耗很多的内存。那么在满足客户需求的前提下,怎么才能尽可能优化一下呢?
2、解决方案
解决以上问题的一个合理的方案就是:代理模式。那么什么是代理模式呢?
定义:为其它对象提供一种代理机制,来控制这个对象的访问操作。
我们可以仔细分析一下上面客户提出的需求,客户要求一次性访问所有数据,这个是客户的需求,要想降低内存的消耗,这个是不可避免的。那么我们只能从减少每条数据的数据量来考虑。一个基本的思路如下:
由于客户访问多条数据的时候,基本上只需要看到用户的姓名,因此可以考虑刚开始从数据库中查询用户数据的,就只是返回用户的姓名和编号就可以,当客户想要查看某一个用户的详细信息的时候,再去数据库获取用户完整数据。这样就可以在满足用户需求的前提下,大大的减少对内存的消耗,是一个用时间换取空间的策略。
可是如何来表示这个只有用户编号和姓名的对象呢?代理模式引入一个Proxy对象来解决这个问题。刚开始只有用户编号和姓名的时候,不是一个完整的用户对象,而是一个代理对象。当需要访问完整的用户数据的时候,代理会总数据库中重新获取相应的数据,通常情况下,只有当客户需要获取除了用户编号和姓名之外的其它额外信息的时候,代理对象才回去到数据库中查询。 小more设计模式———代理模式_第1张图片
  • Proxy——代理对象,通常具有如下功能,实现和具体目标对象一样的接口,这样就可以使用代理来代替具体的目标对象,保存一个指向具体目标对象的引用,再需要的时候调用目标对象。
  • Subject——目标接口,定义代理和具体目标对象的接口,这样就可以在任何使用具体目标对象的地方使用代理对象。
  • RealSubject——具体的目标对象,真正实现接口的功能。
下面我们使用代理模式来实现小more的问题:
首先定一个接口:
小more设计模式———代理模式_第2张图片
然后需要2个实现UserModelApi的类,分别为UserModel和代理类Proxy,UserModel就是一个对接口的真正的实现类,没有其它特别。我们忽略,我们看看代理类Proxy的实现,忽略不重要的方法。
小more设计模式———代理模式_第3张图片
然后我们可以写客户端的实现了,UserManager,主要中心思想有2点
(1)从数据库查询值的时候,不需要全部获取了,只需要查询用户编号和姓名
(2)把数据库中获取的值转变为对象的时候,创建的对象不再是UserModel而是代理Proxy了。而且设置值的时候,不需要全部设置,只需要设置用户编号和姓名就可以。
示例代码如下:
小more设计模式———代理模式_第4张图片
最后我们再写一个客户端就可以了:
小more设计模式———代理模式_第5张图片
好了,使用代理模式解决上面这个问题就搞一个段落,下面总结一下:
(1)其实,在实现的时候,我们发现,这种实现方式有一个潜在的问题,就是如果用户需要大量的查看用户信息的话,那么查询次数就是1 + N次。所以这种适用于用户查询详细信息不多的情况
(2)更加重要的是,比如像Hibernate这类型的ORM框架,在lazy load的情况下,也存在1 + N的查询的情况。其实hibernate的lazy load就是使用代理模式实现的。具体细节可以网上查,相信看完本篇blog,,会很透彻的理解hibernate的lazy load机制。
3、模式分析
  • 代理模式的功能
正是有了代理对象,夹在客户端和真实对象中间,相当于一个中转,那么在中转的时候,就有很多花招可以玩,比如判断一下权限,如果没有足够的权限,那么就不给你中转了。
  • 代理的分类
(1)虚代理——根据需要来创建开销很大的对象,这个对象只有在需要的时候才会创建。
(2)远程代理——用来在不同的地址空间上代表同一个对象,在java里面比较典型的就是RMI技术
(3)copy-on-write代理——在客户端操作的时候,只有对象却是改变了,才会真正的拷贝。
(4)保护代理——控制对原始对象的访问,如果有需要,可以给不同用户提供不同的访问权限
(5)智能指引——在访问对象的时候,执行一些附加的操作,比如,对指向实际对象的引用次数、第一次引用一个持久化对象时,将它装入内存等。
  • 代理模式的本质——控制对象的访问
代理模式通过代理目标对象,把代理对象插入到客户和目标对象之间,从而在它们中间引入一定的间接性。正是这样,给了代理对象很多可以活动的空间。比如权限控制、延迟加载等。
  • 合适选用代理模式
(1)需要为一个对象在不同地址空间提供局部代表(代理)的时候,使用远程代理
(2)某些情况下需要创建开销很大的对象时候,使用虚代理
(3)需要控制对原始对象的访问的时候,使用保护代理
(4)需要在访问对象执行一些附加操作。使用智能指引代理
4、Java中的代理
java对代理模式提供了内建的支持,在java.lang.reflect包下面,提供了一个Proxy的类核一个Invocationhandler的接口。
通常把我们之间实现的代理称为——静态代理。这种实现方式有一个较大的缺点,就是如果subject接口发生变化,那么那么代理类和具体的目标实现类都要变化,不是很灵活。通常把使用java内建的对代理模式支持的功能的代理实现称为Java的动态代理。动态代理和静态代理明显的却别是:静态代理在subject上定义了很多接口,那么代理类也需要定义许多方法。动态代理虽然在subject接口上也定义了许多接口,但是代理类始终只有一个invoke方法,这样即使增加接口,那么代理类也不会变动。
Java的动态代理目前只能代理接口,基本的实现是依靠Java的反射机制和动态生成class技术,来动态生成被代理的接口的实现对象。具体可以参照相关资料。建议研究一下,收获很大。



2018年1月28日 下午16:55
写于北外教室


你可能感兴趣的:(设计模式,设计模式,代理模式)