代码改变世界

4、代理模式与静态代理

2016-04-14 20:15  宏愿。  阅读(341)  评论(0编辑  收藏  举报

 

下面从图上面来理解代理模式:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

如上图,有一套接口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揭秘》