首页 > 代码库 > 设计模式之代理模式学习

设计模式之代理模式学习

代理模式是一种结构型模式,通常源于下边的情况:人们对于复杂的软件系统常常有一种处理手法,即增加一个间接层,从而对系统获得一种更为灵活,满足特定需要的解决方案。例如下图中,B有安全控制或者是缓存控制。B中存在A根本不需要知道的控制方式。A直接使用B,结构上复杂,实现也不方便。A直接使用B的方式,控制不够灵活,可能要用到A根本不知道的方式。增加一个间接层,从而对系统获得更为灵活、满足特定需要的解决方案。假如A需要调用B三次,B比如处理远程的另外一个进程,C做为B的代理人。A和C处于一个地址空间,B可能是一个远程的一个软件,C就象一个缓存一样。在某个触发点的时候,再C与B通信。远程代理。不光实现缓存的效果,屏蔽了分布式通信的细节。 


        我们能通过使用代理模式将有效地向客户端屏蔽细节,降低客户端与系统模块的耦合度,为一个对象提供一个可以连接或操纵它的代理或接口,是软件设计模式中的一项基本技巧。代理模式通常使用在客户端无法直接调用代理所指定的对象,比如位于不同主机中的类之间是无法直接调用的,此时就需要建立代理类并通过代理来调用实际的对象。在代理模式中,有两个对象参与处理同一个请求,接受请求的对象将请求委托给另一个对象来处理,最典型的委托模式是事件处理,由事件源委托给实现了事件监听的对象去处理。委托模式是一项基本技巧,许多其他的模式,如状态模式、策略模式、访问者模式本质上是在更特殊的场合采用了委托模式。“委托”在C#中是一个语言级特性,而在Java语言中没有直接的对应,但是我们可以通过动态代理来实现委托!


      我们举几个例子来说明代理模式:(1)人类在改造自然的过程中,会增加很多间接的工具。比如要挪动一块大石头,我们引入一个间接工具,棍子,只是随着人类进步,棍子变成了千斤顶,铲车。(2)就象记者与明星一样。记者想采访明星,必须通过经纪人,所有记者全直接联系明星,我估计明星,戏也别拍了,歌也别唱了。记者你不能频繁地与骚扰明星。你想约,什么时候约,在什么地点,有什么不该问的。这些细节问题,经纪人一次性会网罗下来,再与明星协商。记者不需要知道明星的电话号码和其它联系方式,一切由经纪人去代理。(3)操作系统就是一个很牛的代理,最早的计算机没有软件,没有程序员,更没有操作系统。增加一些间接的层面。中间软件成为上层软件与硬件。共同的任务,操作系统代理应用软件与硬件。我们就是根本就不知道硬件知识,更加别提机器指令,我们一样通过高层软件驱动硬件工作。(4)直接和数据库打交道非常不好,业务逻辑充斥了SQL代码。所以,金融软件和分布式WEB数据库页面,通常具有中间件来处理前台响应,再适时与后台数据库通信。


       再说到在面向对象中引入代理的动机:在面向对象系统中,有些对象由于某种原因(比如对象创建的开销很大,或者某些操作需要安全控制,或者需要进程外的访问等)直接访问会给使用者、或者系统结构带来很多麻烦。如何在不失去透明操作对象的同时来管理或控制这些对象特有的复杂性?增加一层间接层是软件开发常见的解决方式。JVM,都是间接层。是软件系统中对许许多多复杂问题的一种常见解决方法。在面向对象系统中,直接使用某些对象会带来很多问题,作为间接层的proxy对象便 是解决这一问题的常用手段。


      具体proxy设计模式的实现方法、实现粒度都相关很大,有些可能对单个对象做细粒度的控制,如果copy-0n-write技术,有些可能对组件模块提供抽象代理层,在架构层次对对象做proxy,Proxy并不一定要求保持接口的一致性,只要能够实现间接控制,有时候损及一些透明性是可以接受的。下边就是实现的小代码,就是从前台接收一个数字,后台换算成平方,虽然比较幼稚,但能比较浅显地说明道理:


interface Function{
   void f(int a);
}
class BackRun implements Function{
   public void f(int a){
      System.out.println("对前台回应:" + a*a);
   }
}
public class Proxy implements Function{
   private BackRun backRun;
   public Proxy(){
      if(backRun == null)
          backRun = new BackRun();
   }
    public void f(int a){
       System.out.println("开始连接网络连接..");
       System.out.println("连接成功,处理后台方法");
       backRun.f(a);    //完成代理的使用
    }
}
class ForeRun{
   public static void main(String[] args) {
      int a = 3;
      Function x = new Proxy();
      x.f(a);
   }
}


-----------------------------------------------------------------------------------------
     再分享一个数据库连接池的代理例子吧:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;


public class ConnectionHandler implements InvocationHandler {
    private Connection con;
    private connectionPool pool;
    public ConnectionHandler(connectionPool pool){
        this.pool = pool;
    }
    public Connection bind(Connection con){
        this.con = con;
        Connection proxyCon = (Connection)Proxy.newProxyInstance(con.getClass().getClassLoader(),
                con.getClass().getInterfaces(), this);
        return proxyCon;
    }
    public Object invoke(Object arg0, Method arg1, Object[] arg2)
            throws Throwable {
        // TODO Auto-generated method stub
        Object obj = null;
        if("close".equals(arg1.getName())){
            pool.releaseConnection(con);
        }else{
            obj = arg1.invoke(con, arg2);
        }
        return obj;
    }


}   


     (个人认为现在编程有滥用继承的趋势,JDK 中 Stack 就是一个滥用继承的典型!Stack继承自Vector,其实 Stack 与Vector 在用途上完全是风马牛不相及的两个容器。Stack 和 Vector 的功能是不一样的,Stack 是不允许插入数据到 Stack 任何位置的,但是 Vector 是可以的,如果 Stack 继承自 Vector,那么这样的 Stack 就可以使用 add(int index, E element) 在任何位置插入数据,这明显不符合栈的要求。代码编写有个这样的原则:能不用继承就不用继承,能使用委托实现的就不使用继承。两个类有明显示的层级关系时使用继承,没有明显的层级关系,仅仅是为了在一个类中使用另一个类的方法时应该使用委托。比方说swing的事件处理模型就是一种基于委托的模型,即一个事件源产生一个事件并将它法受到一个或多个监听器那里。监听器等待事件,知道收到一个事件。一旦事件被接受,监听器将会处理这个事件,让后返回相应的值。就是代理, PROXY, 是设计模式的一种.。比如struts1的控制流程就是利用的委托,即ActionServlet接到请求后委托给equestProcessor 去处理不同模块的请求,spring,EJB中的动态代理也都是基于此实现。)