您的位置:首页 > 编程语言 > Java开发

JAVA设计模式之代理模式

2011-12-01 22:32 561 查看
理解并使用设计模式,能够培养我们良好的面向对象编程习惯,同时在实际应用中,可以如鱼得水,享受游刃有余的乐趣.

Proxy是比较有用途的一种模式,而且变种较多,应用场合覆盖从小结构到整个系统的大结构,Proxy是代理的意思,我们也许有代理服务器等概念,代理概念可以解释为:在出发点到目的地之间有一道中间层,意为代理.

设计模式中定义: 为其他对象提供一种代理以控制对这个对象的访问.

为什么要使用Proxy?

1.授权机制 不同级别的用户对同一对象拥有不同的访问权利,如论坛系统中,就可以使用Proxy进行授权机制控制,访问论坛有两种人:注册用户和游客(未注册用户),通过代理来控制这两种用户对论坛的访问权限.

2.某个客户端不能直接操作到某个对象,但又必须和那个对象有所互动.

举例两个具体情况:

(1)如果那个对象是一个是很大的图片,需要花费很长时间才能显示出来,那么当这个图片包含在文档中时,使用编辑器或浏览器打开这个文档,打开文档必须很迅速,不能等待大图片处理完成,这时需要做个图片Proxy来代替真正的图片.

(2)如果那个对象在Internet的某个远端服务器上,直接操作这个对象因为网络速度原因可能比较慢,那我们可以先用Proxy来代替那个对象.

总之原则是,对于开销很大的对象,只有在使用它时才创建,这个原则可以为我们节省很多宝贵的Java内存. 所以,有些人认为Java耗费资源内存,我以为这和程序编制思路也有一定的关系.

代理模式角色

代理模式一般涉及到的角色有:

抽象角色:声明真实对象和代理对象的共同接口;
代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。
真实角色:代理角色所代表的真实对象,是我们最终要引用的对象。

简单实例

以发送消息为例来说明一个简单的代理模式的基本实现:

首先明确目的:有一条消息,需要把这个消息发送出去,根据这个目的定义对应接口MessageHandler。需要的附加操作:假设需要验证消息的长度不能超过指定长度并且不能为空,并且我们需要统计相关信息发送到次数,超过指定的次数我们需要输出警报。我们通过代理模式来实现这个附加的操作。下面为对应的类关系图及示例代码。

//接口定义

public interface MessageHandler {

public void sendMessage(String msg);

}

//通过Email方式发送消息的实现类

public class EmailMessage implements MessageHandler {

@Override

public void sendMessage(String msg) {

// TODO Auto-generated method stub

System.out.println(msg+" send!!");

}

}

//消息处理的代理类

public class MessageProxy implements MessageHandler {

private static int count;

private MessageHandler emailMsg;

@Override

public void sendMessage(String msg) {

// TODO Auto-generated method stub

if(checkMessage(msg))

{

if(emailMsg==null) emailMsg=new EmailMessage();

count++;

emailMsg.sendMessage(msg);

System.out.println("Message sent:"+count);

}

}

private boolean checkMessage(String msg) {

return msg != null && msg.length() > 10;

}

}

//调用类

public class MainClass {

private static void runProxy(MessageHandler handler)

{

handler.sendMessage("message for test");

}

/**

* @param args

*/

public static void main(String[] args) {

// TODO Auto-generated method stub

runProxy(new EmailMessage());

System.out.println("++++++++++++++++Pjroxy++++++++++++++++++");

runProxy(new MessageProxy());

}

}

输出

message for test send!!

++++++++++++++++Pjroxy++++++++++++++++++

message for test send!!

Message sent:1

在例子中我们可以方便的在消息发送过程中添加各种需要的附加处理方式,也能方便的替换消息的处理方式,如将通过Email发送消息替换为通过短信发送消息,而调用方不会有丝毫察觉!在任何你想要将一些额外操作分离到具体对象之外,特别是希望能够很容易做出修改,或者想在具体对象的方法执行前插入一些额外操作的时候,代理就显得十分有用!

动态代理

java中动态代理机制的引入使得代理模式的思想更加完善与进步,它允许动态的创建代理并支持对动态的对所代理的方法进行调用。Java动态代理类位于Java.lang.reflect包下,一般主要涉及到以下两个类:

(1). Interface InvocationHandler:该接口中仅定义了一个方法Object:invoke(Object proxy,Method method, Object[] args)。

参数:

proxy - 在其上调用方法的代理实例

method - 对应于在代理实例上调用的接口方法的 Method 实例。Method 对象的声明类将是在其中声明方法的接口,该接口可以是代理类赖以继承方法的代理接口的超接口。

args - 包含传入代理实例上方法调用的参数值的对象数组,如果接口方法不使用参数,则为 null。基本类型的参数被包装在适当基本包装器类(如 java.lang.Integer 或 java.lang.Boolean)的实例中。

返回:

从代理实例的方法调用返回的值。如果接口方法的声明返回类型是基本类型,则此方法返回的值一定是相应基本包装对象类的实例;否则,它一定是可分配到声明返回类型的类型。如果此方法返回的值为 null 并且接口方法的返回类型是基本类型,则代理实例上的方法调用将抛出 NullPointerException。否则,如果此方法返回的值与上述接口方法的声明返回类型不兼容,则代理实例上的方法调用将抛出 ClassCastException。

(2).Proxy:该类即为动态代理类,作用类似于上例中的ProxySubject,其中主要包含以下内容:

Protected Proxy(InvocationHandler h):构造函数,估计用于给内部的h赋值。

Static Class getProxyClass (ClassLoader loader, Class[] interfaces):获得一个代理类,其中loader是类装载器,interfaces是真实类所拥有的全部接口的数组。

Static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h):返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类的在Subject接口中声明过的方法)。

例子代码:

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Proxy;

import java.lang.reflect.Method;

public class DynamicProxyTest {

public static Object proxy;

public static void main(String[] args) {

HelloWorld hw = new HelloWorldImpl();

InvocationHandler handler = new HelloWorldHandler(hw);

HelloWorld proxy = (HelloWorld) Proxy.newProxyInstance(hw.getClass()

.getClassLoader(), hw.getClass().getInterfaces(), handler);

proxy.sayHelloWorld();

//判断是否生成的代理对象

System.out.println(DynamicProxyTest.proxy == proxy);

}

}

class HelloWorldHandler implements InvocationHandler {

// 要代理的原始对象

private Object objOriginal;

/**

* 构造函数。

*

* @param obj

* 要代理的原始对象。

*/

public HelloWorldHandler(Object obj) {

this.objOriginal = obj;

}

public Object invoke(Object proxy, Method method, Object[] args)

throws Throwable {

Object result;

// 方法调用之前

doBefore();

//把这个proxy的引用保存在DynamicProxyTest.proxy,以便之后判断是否是生成的代理对象

DynamicProxyTest.proxy = proxy;

// 调用原始对象的方法

result = method.invoke(this.objOriginal, args);

// 方法调用之后

doAfter();

return result;

}

private void doBefore() {

System.out.println("before method invoke!");

}

private void doAfter() {

System.out.println("after method invoke!");

}

}

class HelloWorldImpl implements HelloWorld {

public void sayHelloWorld() {

System.out.println("Hello World!");

}

}

interface HelloWorld {

void sayHelloWorld();

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: