【设计模式】动态代理Proxy_01

原创 2015年07月10日 11:01:13
大家都知道设计模式中一个比较难理解的模式--动态代理模式,下面我们来通过一步一步完善一个工程来学习动态代理。

首先我们创建一个JavaProject,名字为"ProxyTest"。

创建一个类Tank.java,是一个坦克类,然后我们创建一个接口Moveable.java
Moveable.java:
package cn.edu.hpu.proxy;

public interface Moveable {
 void move();
}

Tank类实现Moveable接口
Tank.java:
package cn.edu.hpu.proxy;

import java.util.Random;

public class Tank implements Moveable{

  @Override
  public void move() {
    System.out.println("坦克正在移动中...");
      try {
            //睡眠10秒以内的随机时间,表示坦克正在移动中
            Thread.sleep(new Random().nextInt(10000));
          } catch (InterruptedException e) {
          // TODO Auto-generated catch block
              e.printStackTrace();
          }
  }

}

下面问题来了:
我想知道一个方法的运行时间,如何得到?

大家很容易想到的是,在方法刚刚执行后和就要结束之前计算一下当前时间,然后相减获得执行时间:

@Override
public void move() {
	long start=System.currentTimeMillis();
	System.out.println("坦克正在移动中...");
	try {
		//睡眠10秒以内的随机时间,表示坦克正在移动中
		Thread.sleep(new Random().nextInt(10000));
	} catch (InterruptedException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
	long end=System.currentTimeMillis();
	System.out.println("运行时间:"+(end-start)+"ms");
}
那么,如果不允许修改move中的方法呢?比如说这个方法是别人的包里提供给你的,是人家已经写好的源代码,就编译了class给你,就没有给你源码,你没法在源码上修改怎么办?解决这件事情之前,我们先写一个测试类:
package cn.edu.hpu.proxy;

public class Client {
    public static void main(String[] args) {
         Moveable m=new Tank();
         m.move();
    }
}
不要求计算move()方法执行的时间(JDK为其准备的时间等...),要计算内部代码执行的时间。

我们再写一个Tank2,继承Tank
package cn.edu.hpu.proxy;

public class Tank2 extends Tank{

     @Override
     public void move() {
         long start=System.currentTimeMillis();
         super.move();
         long end=System.currentTimeMillis();
         System.out.println("运行时间:"+(end-start)+"ms");
     }

}
用继承来实现把原来的方法前后加一些逻辑,这是一种方式,是没有问题的。

除了上面这种方式之外,还有另外一种方式:
写一个Tank3,实现和Tank一样的接口
package cn.edu.hpu.proxy;

public class Tank3 implements Moveable{
	Tank t;

	public Tank3(Tank t) {
		super();
		this.t = t;
	}

	@Override
	public void move() {
		long start=System.currentTimeMillis();
		t.move();
		long end=System.currentTimeMillis();
		System.out.println("运行时间:"+(end-start)+"ms");
		
	}

}
上面一个使用继承来实现,一个是一个类存有另外一个类的对象,叫聚合。
这两种都是简单的代理的实现。

现在请问大家:认为这两种方式那种更好?为什么?
聚合好。为什么呢?
下面来看看。

除了时间的代理之外,还有其他的各种各样的代理,如在方法开始前后做日志记录,或加一段事务控制,或检查运行权限等。

由于Tank3它是记录时间的,而且是Tank的一个代理类,所以我们给它换一个名字,叫"TankTimeProxy"类。

package cn.edu.hpu.proxy;

public class TankTimeProxy implements Moveable{
	Tank t;

	public TankTimeProxy(Tank t) {
		super();
		this.t = t;
	}

	@Override
	public void move() {
		long start=System.currentTimeMillis();
		System.out.println("开始时间:"+start+"ms");
		t.move();
		long end=System.currentTimeMillis();
		System.out.println("运行时间:"+(end-start)+"ms");
		
	}

}


我们再写一个类是对Tank类加日志信息的日志代理类:

package cn.edu.hpu.proxy;

public class TankLogProxy implements Moveable{
	Tank t;

	public TankLogProxy(Tank t) {
		super();
		this.t = t;
	}

	@Override
	public void move() {
		System.out.println("坦克准备移动...");
		t.move();
		System.out.println("坦克移动结束...");
	}

}
下面我们要对日志进行叠加,规定先先记录时间,再记录日志,怎么办?需要在写一个Tank3去继承Tank2(里面已经包含了时间记录代理),然后把日志记录代码加在move方法前后。如果规定先记录日志,再记录时间,怎么办?又要创建一个Tank4,继承Tank类,然后把日志记录代码加在move方法前后,之后再创建Tank5继承自Tank4,在在move方法前后加上时间记录代码.....你会发现,用继承的方式实现代理的话,这个类会无限制的增加下去,各种各样的代理功能如果想实现叠加的话,这个类要无限的往下继承,无边无际了...

所以,回到刚刚的话题,继承并不是好的实现代理的方式,聚合比较合适。
接下来看看聚合的优点。首先我们把TankTimeProxy和TankLogProxy中的Tank引用类改成Moveable接口(包括构造函数的参数类型)。
拿出TankTimeProxy与TankLogProxy看看:

TankTimeProxy:

package cn.edu.hpu.proxy;

public class TankTimeProxy implements Moveable{
	Moveable t;

	public TankTimeProxy(Moveable t) {
		super();
		this.t = t;
	}

	@Override
	public void move() {
		long start=System.currentTimeMillis();
		System.out.println("开始时间:"+start+"ms");
		t.move();
		long end=System.currentTimeMillis();
		System.out.println("运行时间:"+(end-start)+"ms");
		
	}

}

TankLogProxy:
package cn.edu.hpu.proxy;

public class TankLogProxy implements Moveable{
	Tank t;

	public TankLogProxy(Tank t) {
		super();
		this.t = t;
	}

	@Override
	public void move() {
		System.out.println("坦克准备移动...");
		t.move();
		System.out.println("坦克移动结束...");
	}

}


我们其中的引用类Moveable t;既可以是实现了Moveable接口的Tank类,又可以是实现了Moveable接口的代理类,那么,代理之间的嵌套是不是可以实现呢?可以,加入我们想在日志代理外面嵌套时间代理,即先记录时间后记录日志,那么我们直接把引用类Moveable t;的实现指向日志代理类TankLogProxy即可。
即:
Moveable t=new TankTimeProxy(new TankLogProxy(new Tank()));
t.move();
运行TankTimeProxy的move方法时,
@Override
public void move() {
long start=System.currentTimeMillis();
System.out.println("开始时间:"+start+"ms");
//此时t指向TankLogProxy类,调用的是TankLogProxy类的move方法
t.move();
long end=System.currentTimeMillis();
System.out.println("运行时间:"+(end-start)+"ms");

}
我们知道TankLogProxy类的move方法是这样的(也就是上面的t.move();):
@Override
public void move() {
System.out.println("坦克准备移动...");
//这里的t指向的是Tank,所以执行的是Tank的move方法
t.move();
System.out.println("坦克移动结束...");
}


把TankTimeProxy的move方法中的t.move();解刨出来就是:
</pre><pre name="code" class="java">@Override
public void move() {
	long start=System.currentTimeMillis();
	System.out.println("开始时间:"+start+"ms");


	System.out.println("坦克准备移动...");
	//Tank的move方法
	t.move();
	System.out.println("坦克移动结束...");


	long end=System.currentTimeMillis();
	System.out.println("运行时间:"+(end-start)+"ms");
		
}

注意:此时Tank类(没加任何代理,只有最原始的移动方法):
package cn.edu.hpu.proxy;

import java.util.Random;

public class Tank implements Moveable{

	@Override
	public void move() {
		System.out.println("坦克正在移动中...");
		try {
			//睡眠10秒以内的随机时间,表示坦克正在移动中
			Thread.sleep(new Random().nextInt(10000));
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
}

测试:
package cn.edu.hpu.proxy;

public class Client {
	public static void main(String[] args) {
		Moveable t=new TankTimeProxy(new TankLogProxy(new Tank()));
		t.move();
	}
}

测试结果:
开始时间:1435309953538ms
坦克准备移动...
坦克正在移动中...
坦克移动结束...
运行时间:1529ms

此时就实现了时间和日志代理的嵌套,当然还可以嵌套更多的代理逻辑,是不是比继承方便多了?


以后为了方便管理,也可以将代理逻辑的先后通过配置文件来配置。
关键就是实现统一接口。

我们接下来继续讨论(以后只考虑TankTimeProxy)。如果Moveable接口里还有一个方法,stop()停止方法,Tank必须实现这个方法,那么我要知道stop()方法运行的时间,还要加之前写的时间代理代码:
首先是Moveable接口
package cn.edu.hpu.proxy;

public interface Moveable {
	void move();
	void stop();
}

Tank类
package cn.edu.hpu.proxy;

import java.util.Random;

public class Tank implements Moveable{

	@Override
	public void move() {
		System.out.println("坦克正在移动中...");
		try {
			//睡眠10秒以内的随机时间,表示坦克正在移动中
			Thread.sleep(new Random().nextInt(10000));
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	@Override
	public void stop() {
		System.out.println("坦克停止中...");
		
	}
	
}

TankTimeProxy类
package cn.edu.hpu.proxy;


public class TankTimeProxy implements Moveable{
	Moveable t;


	public TankTimeProxy(Moveable t) {
		super();
		this.t = t;
	}


	@Override
	public void move() {
		long start=System.currentTimeMillis();
		System.out.println("开始时间:"+start+"ms");
		t.move();
		long end=System.currentTimeMillis();
		System.out.println("运行时间:"+(end-start)+"ms");
		
	}


	@Override
	public void stop() {
		long start=System.currentTimeMillis();
		System.out.println("开始时间:"+start+"ms");
		t.stop();
		long end=System.currentTimeMillis();
		System.out.println("运行时间:"+(end-start)+"ms");
	}
}

这个时候我们注意了,当我们平时在写程序的时候,如果你发现有一段代码你总是在那里写,这个方法里有,那个方法里也有,这个时候我们就要考虑将这段代码封装起来了。
这里我们发现TankTimeProxy类中有关时间记录的代码有重复,我们将其封装起来:
package cn.edu.hpu.proxy;

public class TankTimeProxy implements Moveable{
	Moveable t;
	long start;
	long end;

	public TankTimeProxy(Moveable t) {
		super();
		this.t = t;
	}

	public void before(){
		start=System.currentTimeMillis();
		System.out.println("开始时间:"+start+"ms");
	}
	
	public void after(){
		end=System.currentTimeMillis();
		System.out.println("运行时间:"+(end-start)+"ms");
	}
	
	@Override
	public void move() {
		this.before();
		t.move();
		this.after();
	}


	@Override
	public void stop() {
		this.before();
		t.stop();
		this.after();
	}
}

下面我们考虑一个很难的问题,假如说我想让TankTimeProxy这个代理叫做TimeProxy,意味着它不仅可以计算Tank类的运行方法,还可以把任何一个对象当做被代理对象,可以计算任何代理类的方法运行的时间。(至于为什么会有这种需求,比如说我们又加了一个交通工具Car,我们要对Car计算运行时间的话,要重写一个CarTimeProxy代理,如果还有上百种类型的类,对不同的类实现时间代理,你要写上百种代理类!!!)。

所以我们要实现一种"通用"时间代理,可以对任意对象代理。

我们可以使用Java的反射机制,具体方式详见总结02

转载请注明出处:http://blog.csdn.net/acmman/article/details/46827819

版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

HDU 1237 简单计算器

简单计算器 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Sub...

【JSP开发】一个防盗链的WEB小例子

有的资源你点出后会有广告,广告旁边是资源连接,有些人直接把资源连接发给别人,企图不看广告直接进入链接拿资源,为了防止盗链行为的发生,我们要检测用户访问url的情况来进行一系列措施。 需要实现的功能就是...

设计模式之动态代理-proxy

动态代理作为代理模式的一种扩展形式,广泛应用于框架(尤其是基于AOP的框架)的设计与开发,本文将通过实例来讲解Java动态代理的实现过程。        友情提示:本文略有难度,读者需具备代理模式相...

Java设计模式Proxy之动态代理

Java动态代理主要涉及到两个类: InvocationHandler:该接口中仅定义了一个Object : invoke(Object proxy, Method method, Object...

java设计模式:动态代理模式 Proxy

代理模式基本上可以理解为:本来让A做一件事情,可以把事情交给A的代理B去处理,B不仅仅把A该做的做掉,还可以在A该做的事情的基础上在做一些相关的事情; 所谓动态代理就是利用Java的反射机制,由程序...
  • sidihuo
  • sidihuo
  • 2016年02月25日 15:16
  • 241

设计模式之Proxy(代理):模拟JDK的动态代理

1.静态代理     代理的实现方式有两种,一是继承,二是聚合。示例如下:计算Tank中move方法的运行时间,不包括JDK为其准备运行环境的时间。 2.动态代理     如果有很多对象,要想对任意...

《大话设计模式》读书笔记:代理模式与Java的Proxy动态代理

代理模式(Proxy),为其他对象提供一种代理以控制对这个对象的访问。 在面向对象编程中,直接引用某些对象会因为种种原因(比如对象创建的开销过大,访问需要安全控制,或者需要跳出当前进程等)带来很多问...

设计模式—静态代理模式和动态代理模式(Proxy Pattern)

一、代理模式 所谓代理,就是一个人或者一个机构代表另一个人或者另一个机构采取行动。在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之前起到中介的作用。代理模式给某...

动态代理设计模式

  • 2012年09月27日 09:33
  • 9KB
  • 下载

基础加强:类加载器 ---注解 ---动态代理(装饰设计模式)

基础加强 1.类加载器 2.注解 3.动态代理(装饰设计模式) 1.类加载器,作用? 见图1,2  怎么获取类加载器?(重点)      ClassLoader  字节码对象.getCla...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:【设计模式】动态代理Proxy_01
举报原因:
原因补充:

(最多只允许输入30个字)