下面从图上面来理解代理模式:
如上图,有一套接口ISubject,其实现类是SubjectImpl。
通常情况下采用面向接口编程,new 一个SubjectImpl并将其赋值给ISubject类型的引用,后续都通过接口类型的引用来调用实现类的具体方法。这是“面向接口编程”和“多态”的实际应用。如下:
code1:
ISubject sub = new SubjectImpl(); //实现类对象赋值给接口引用 sub.request(); //通过接口调用方法
代理模式通常涉及到4种角色:
1、ISubject。接口,是被访问资源的抽象。
2、SubjectImpl。具体的实现类,者是被访问资源的具体实现类。
3、SubjectProxy。被访问资源的代理实现类,注意:该类中持有一个ISubject接口的具体实例。如上图中,SubjectProxy要对SubjectImpl实现类进行代理,那么其持有的具体实例就是SubjectImpl。
4、Client。代表访问者的抽象角色,Client会访问ISubject类型的对象或资源。
我们应该要注意到:SubjectImpl和SubjectProxy都实现了公共的接口ISubject,而SubjectProxy内部持有一个SubjectImpl(或者是ISubject接口的其它实现类)的引用。当Client调用request()方法访问资源的时候,SubjectProxy会通过其内部的持有对象将请求转发给SubjectImpl来执行。
但从请求的角度来看,这似乎是一个多余的过程,还没有code1来得简单直接。为什么还要这么做呢??
现在我们给request()方法执行加一个条件:每天的0点到6点之间,系统不接受Client的request()。
解决方案1:在code1调用sub.request()之前判断
1 ISubject sub = new SubjectImpl(); 2 3 Date start = DateUtils.timeOfDay(0,0,0); //时间0点 4 Date end = DateUtils.timeOfDay(6,0,0); //6点 5 Date end = new Date(); 6 if(now.after(start)&&now.before(end)){ 7 System.out.println("系统维护时间,禁止请求..."); 8 return null; //不会执行被代理对象的实际方法 9 }else{ 10 sub.request(); //特定时间之外执行请求 11 }
这样做当然能实现,但是我们必须在工程中所有调用sub.request()的地方都写上这么多冗余的代码,如果有一天时间从6点推迟到7点了,那么我们必须手动的修改散落在各个角落的源代码,维护成本很高。
有童鞋说,好办,修改下SubjectImpl的源代码不就什么事情都解决了吗?
嗯,这是饮鸩止渴的做法,修改SubjectImpl源代码有几个不妥的地方:①不一定所有的sub.request()方法都需要检查时间;②有的源码包不是你想改就能改的;
代理设计模式为我们提供了方案:设计一个代理类SubjectProxy,其内部持有一个SubjectImpl的引用,相当于在SubjectImpl外面做了一个包装。Client不直接操作SubjectImpl,而是操作代理类SubjectProxy的对象,在其内部转发请求之前和之后都可以插入一些额外的操作。
1 public class SubjectProxy implements ISubject{ 2 private ISubject proxied; 3 public void bind(ISubject sub){ 4 this.proxied = sub; 5 } 6 7 public void request(){ //重写接口的request方法 8 Date start = new Date(DateUtil.timeOfToday(0, 0, 0)); 9 Date end = new Date(DateUtil.timeOfToday(5, 59, 59)); 10 Date now = new Date(); 11 if(now.after(start)&&now.before(end)){ 12 System.out.println("系统维护时间,禁止请求..."); 13 return; //不会执行被代理对象的实际方法 14 } 15 16 proxied.request(); //转发请求 17 } 18 }
用户持有并操作代理类对象:
1 static public void main(String[] args){ 2 ISubject proxy = new SubjectProxy(); //new一个代理对象,并向上转型 3 proxy.bind(new SubjectImpl()); //将一个SubjectImpl对象绑定到代理对象上 4 //用户通过代理对象来访问具体的SubjectImpl资源 5 proxy.request(); 6 }
这样在用户调用proxy.request()方法的时候,首先会执行SubjectProxy类中的request方法,此方法在实际调用SubjectImpl的request方法之前会作时间判断。
上面的这种代理模式称为是静态代理模式。其特点是,需要为每一个接口都对应的设计一个代理类。这样,当有成百上千个接口的时候,就对应着有成百上千个代理类!!!
与之相对应的是动态代理。后面继续分析...
参考:《Spring揭秘》