第15章:代理模式-通过代理对象访问
定义:
代理模式(Proxy Pattern):给某一个对象提供一个代理或占位符,并由代理对象来控制对原对象的访问。
结构:
代码实现:
//抽象主题角色
abstract class Subject {
public abstract void Request();
}
//真实主题
class RealSubject extends Subject {
@Override
public void Request() {
//业务方法具体实现代码
}
}
//代理类
class Proxy extends Subject {
private RealSubject realSubject = new RealSubject(); //维持一个对真实主题对象
public void PreRequest() {
//…...
}
public void Request() {
PreRequest(); //额外功能
realSubject.Request(); //调用真实主题对象的方法
PostRequest();
}
public void PostRequest() {
//……
}
}
远程代理(Remote Proxy):
为一个位于不同的地址空间的对象提供一个本地的代理对象,这个不同的地址空间可以是在同一台主机中,也可是在另一台主机中,远程代理又称为大使(Ambassador)。
虚拟代理(Virtual Proxy):
如果需要创建一个资源消耗较大的对象,先创建一个消耗相对较小的对象来表示,真实对象只在需要时才会被真正创建。
保护代理(Protect Proxy):
控制对一个对象的访问,可以给不同的用户提供不同级别的使用权限。
缓冲代理(Cache Proxy):
为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果。
智能引用代理(Smart Reference Proxy):
当一个对象被引用时,提供一些额外的操作,例如将对象被调用的次数记录下来等。在这些常用的代理模式中,有些代理类的设计非常复杂,例如远程代理类,它封装了底层网络通信和对远程对象的调用,其实现较为复杂。
应用实例:
真实对象只实现商务信息查询,代理对象增加身份验证和日志记录功能
//抽象查询类
public interface Searcher {
public String doSearch(String userId,String keyword);
}
//真实查询
public class RealSearcher implements Searcher{
@Override
public String doSearch(String userId, String keyword) {
System.out.println("用户"+userId+"使用关键字"+keyword+"查询信息");
return "返回真实内容";
}
}
//代理查询,同时实现用户验证和日志记录
public class ProxySearcher implements Searcher{
private RealSearcher searcher=new RealSearcher();
private AccessValidator validator;
private Logger logger;
@Override
public String doSearch(String userId, String keyword) {
if (this.validate(userId)){
String result = searcher.doSearch(userId, keyword);
this.log(userId);
return result;
}else {
return null;
}
}
public boolean validate(String userId){
validator=new AccessValidator();
return validator.validate(userId);
}
public void log(String userId){
logger=new Logger();
logger.log(userId);
}
}
//日志记录
public class Logger {
public void log(String userId){
System.out.println("更新数据库,用户"+userId+"查询次数加1");
}
}
//验证用户
public class AccessValidator {
public boolean validate(String userId){
System.out.println("在数据库中验证用户"+userId+"是否为合法用户?");
if (userId.equalsIgnoreCase("杨过")){
System.out.println(userId+"登录成功");
return true;
}
else {
System.out.println(userId+"登录失败");
return false;
}
}
}
public class Client {
public static void main(String[] args) {
Searcher searcher;
searcher= (Searcher) XMLUtils.getBean();
searcher.doSearch("杨过","玉女心经");
}
}
<?xml version="1.0" encoding="UTF-8" ?>
<config>
<className>proxy.ProxySearcher</className>
</config>
//XMLUtils
public class XMLUtils {
public static Object getBean(){
try {
DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = dFactory.newDocumentBuilder();
Document doc;
doc =builder.parse(new File("proxy//src//proxy//config.xml"));
NodeList nl = doc.getElementsByTagName("className");
Node classNode = nl.item(0).getFirstChild();
String cName = classNode.getNodeValue();
Class<?> c = Class.forName(cName);
Object obj = c.newInstance();
return obj;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
优点:
- 能够协调调用者和被调用者,在一定程度上降低了系统的耦合度。
- 客户端可以针对抽象主题角色进行编程,增加和更换代理类无须修改源代码,符合开闭原则,系统具有较好的灵活性和可扩展性。
- 远程代理为位于两个不同地址空间对象的访问提供了一种实现机制,可以将一些消耗资源较多的对象和操作移至性能更好的计算机上,提高系统的整体运行效率。
- 虚拟代理通过一个消耗资源较少的对象来代表一个消耗资源较多的对象,可以在一定程度上节省系统的运行开销。
- 缓冲代理为某一个操作的结果提供临时的缓存存储空间,以便在后续使用中能够共享这些结果,优化系统性能,缩短执行时间。
- 保护代理可以控制对一个对象的访问权限,为不同用户提供不同级别的使用权限。
缺点:
- 由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢,例如保护代理。
- 实现代理模式需要额外的工作,而且有些代理模式的实现过程较为复杂,例如远程代理。
适用场景:
- 当客户端对象需要访问远程主机中的对象时可以使用远程代理。
- 当需要用一个消耗资源较少的对象来代表一个消耗资源较多的对象,从而降低系统开销、缩短运行时间时可以使用虚拟代理,例如一个对象需要很长时间才能完成加载时。
- 当需要为某一个被频繁访问的操作结果提供一个临时存储空间,以供多个客户端共享访问这些结果时可以使用缓冲代理。通过使用缓冲代理,系统无须在客户端每一次访问时都重新执行操作,只需直接从临时缓冲区获取操作结果即可。
- 当需要控制对一个对象的访问,为不同用户提供不同级别的访问权限时可以使用保护代理。
- 当需要为一个对象的访问(引用)提供一些额外的操作时可以使用智能引用代理。