col.add(um);
}
rs.close();
pstmt.close();
}finally{
conn.close();
}
return col;
}
/**
* 获取与数据库的连接
* @return 数据库连接
*/
private Connection getConnection() throws Exception {
Class.forName("你用的数据库对应的JDBC驱动类");
return DriverManager.getConnection(
"连接数据库的URL", "用户名", "密码");
}
}
(3)写个客户端来测试看看,是否能满足功能要求,示例代码如下:
| public class Client { public static void main(String[] args) throws Exception{ UserManager userManager = new UserManager(); Collection
userManager.getUserByDepId("0101"); System.out.println(col); } } |
运行结果如下:
| [userId=user0001,name=张三1,depId=010101,sex=男 , userId=user0002,name=张三2,depId=010101,sex=男 , userId=user0003,name=张三3,depId=010102,sex=男 ] |
你还可以修改getUserByDepId的参数,试试传递不同的参数,然后再看看输出的值,看看是否正确的实现了要求的功能。
11.1.3 有何问题
上面的实现看起来很简单,功能也正确,但是蕴含一个较大的问题,那就是:当一次性访问的数据条数过多,而且每条描述的数据量又很大的话,那会消耗较多的内存。
前面也说了,对于用户表,事实上是有很多字段的,不仅仅是示例的那么几个,再加上不使用翻页,一次性访问的数据就可能会有很多条。如果一次性需要访问的数据较多的话,内存开销会比较大。
但是从客户使用角度来说,有很大的随机性,客户既可能访问每一条数据,也可能一条都不访问。也就是说,一次性访问很多条数据,消耗了大量内存,但是很可能是浪费掉了,客户根本就不会去访问那么多数据,对于每条数据,客户只需要看看姓名而已。
那么该怎么实现,才能既把多条用户数据的姓名显示出来,而又能节省内存空间,当然还要实现在客户想要看到更多数据的时候,能正确访问到数据呢?
11.2 解决方案
11.2.1 代理模式来解决
用来解决上述问题的一个合理的解决方案就是代理模式。那么什么是代理模式呢?
(1)代理模式定义
(2)应用代理模式来解决的思路
仔细分析上面的问题,一次性访问多条数据,这个可能性是很难避免的,是客户的需要。也就是说,要想节省内存,就不能从减少数据条数入手了,那就只能从减少每条数据的数据量上来考虑。
一个基本的思路如下:由于客户访问这多条用户数据的时候,基本上只需要看到用户的姓名,因此可以考虑刚开始从数据库查询返回的用户数据就只有用户编号和用户姓名,当客户想要详细查看某个用户的数据的时候,再次根据用户编号到数据库中获取完整的用户数据。这样一来,就可以在满足客户功能的前提下,大大减少对内存的消耗,只是每次需要重新查询一下数据库,算是一个以时间换空间的策略。
可是该如何来表示这个只有用户编号和姓名的对象呢?它还需要实现在必要的时候访问数据库去重新获取完整的用户数据。
代理模式引入一个Proxy对象来解决这个问题,刚开始只有用户编号和姓名的时候,不是一个完整的用户对象,而是一个代理对象,当需要访问完整的用户数据的时候,代理会从数据库中重新获取相应的数据,通常情况下是当客户需要访问除了用户编号和姓名之外的数据的时候,代理才会重新去获取数据。
11.2.2 模式结构和说明
代理模式的结构如图11.1所示:

图11.1 代理模式的结构示意图
Proxy:
代理对象,通常具有如下功能:
- 实现与具体的目标对象一样的接口,这样就可以使用代理来代替具体的目标对象
- 保存一个指向具体目标对象的引用,可以在需要的时候调用具体的目标对象
- 可以控制对具体目标对象的访问,并可能负责创建和删除它
Subject:
目标接口,定义代理和具体目标对象的接口,这样就可以在任何使用具体目标对象的地方使用代理对象
RealSubject:
具体的目标对象,真正实现目标接口要求的功能。
在运行时刻一种可能的代理结构的对象图如图11.2所示:

图11.2 运行时刻一种可能的代理结构的对象图
11.2.3 代理模式示例代码
(1)先看看目标接口的定义,示例代码如下:
/**
* 抽象的目标接口,定义具体的目标对象和代理公用的接口
*/
public interface Subject {
/**
* 示意方法:一个抽象的请求方法
*/
public void request();
}
(2)接下来看看具体目标对象的实现示意,示例代码如下:
/**
* 具体的目标对象,是真正被代理的对象
*/
public class RealSubject implements Subject{
public void request() {
//执行具体的功能处理
}
}
(3)接下来看看代理对象的实现示意,示例代码如下:
/**
* 代理对象
*/
public class Proxy implements Subject{
/**
* 持有被代理的具体的目标对象
*/
private RealSubject realSubject=null;
/**
* 构造方法,传入被代理的具体的目标对象
* @param realSubject 被代理的具体的目标对象
*/
public Proxy(RealSubject realSubject){
this.realSubject = realSubject;
}
public void request() {
//在转调具体的目标对象前,可以执行一些功能处理
//转调具体的目标对象的方法
realSubject.request();
//在转调具体的目标对象后,可以执行一些功能处理
}
}
11.2.4 使用代理模式重写示例
要使用代理模式来重写示例,首先就需要为用户对象定义一个接口,然后实现相应的用户对象的代理,这样在使用用户对象的地方,就使用这个代理对象就可以了。
这个代理对象,在起初创建的时候,只需要装载用户编号和姓名这两个基本的数据,然后在客户需要访问除这两个属性外的数据的时候,才再次从数据库中查询并装载数据,从而达到节省内存的目的,因为如果用户不去访问详细的数据,那么那些数据就不需要被装载,那么对内存的消耗就会减少。
先看看这个时候系统的整体结构,如图11.3所示:

图11.3 代理模式重写示例的系统结构示意图
此时的UserManager类,充当了标准代理模式中的Client的角色,因为是它在使用代理对象和用户数据对象的接口。
还是看看具体的代码示例,会更清楚。
(1)先看看新定义的用户数据对象的接口,非常简单,就是对用户数据对象属性操作的getter/setter方法,因此也没有必要去注释了,示例代码如下:
/**
* 定义用户数据对象的接口
*/
public interface UserModelApi {
public String getUserId();
pu