`
行者买刀
  • 浏览: 194551 次
  • 性别: Icon_minigender_1
  • 来自: 厦门
社区版块
存档分类
最新评论

设计模式之略见一斑(代理模式Proxy)

    博客分类:
  • J2SE
阅读更多

普通对象所需要完成的任务就是通过公共接口为外界提供自己所承诺的服务。然而,有时候合法的对象可能会因为各种原因而无法完成自己常规的任务。尤其是当对象需要长时候才能载入内存、对象正运行在另一台计算机上或者需要获取对象消息的时候,这种情况会频繁出现。在这种情况下,我们可以使用一个代理对象,由它来承担客户期待的责任,并将请求转发给其背后的目标对象。

 

Proxy模式的意图在于为对象提供一个代理或者占位来控制该对象的访问.

 

下面对代理对象的各种情况进行一一分析,它分别有

图像代理,远程代理,动态代理

 

1)图像代理

    代理通常拥有与实际对象基本相同的接口。代理的工作方式是:把服务请求明智地转发给代理控制的底层对象,最终完成任务。避免将较大的图像加载到内存就是一个典型的例子。假定我们要加载一张较大的图像,我们需要为这张图像建立代理,通过先加载一张显示loading的小图片,然后当大图像已完全加载到内存时,代理通知替换这张loading小图片。

 

代码如下:

public class ImageProxy implements Runnable{
	static final ImageIcon BLACK = new ImageIcon("images/black.jpg");
     static final ImageIcon LOADING = new ImageIcon("images/loading.jpg");
     protected String filename;
     protected JFrame jf;
     ImageIcon currentImage = BLACK;
     public ImageProxy(String filename){
    	 this.filename = filename;
     }
     public void load(JFrame jf){
    	 this.jf = jf;
    	 currentImage = LOADING;
    	 jf.repaint();
    	 new Thread(this).start();
     }
     public void run(){
    	 currentImage = new ImageIcon(ClassLoader.getSystemResource(filename));
    	 jf.pack();
     }
     ..
     ..
}

 代理类ImageProxy通过线程方式,当大图片加载完成时就替换Loading状态的图片

 

 

2)远程代理

 

   如果我们期望调用正在另一台计算机上运行的对象的方法,那么必须找到一种方法来与该远程对象进行通信,而不能直接调用其方法。我们可以在远程机器上打开一个套接字,并设计一种协议用于向该远程对象发送消息。理想情况下,这种方案可以让我们自由地与远程对象进行通信,就像与本地对象进行通信一样。在这种方案下使用代理模式,可直接调用位于本地的代理对象的方法,该代理对象将调用请求发给远程对象。实际上,著名的公共对象请求代理架构(CORBA),ASP.NET以及JAVA 的远程方法调用(RMI)已经实现了这种方案。

   在RMI中代理对象用于将调用请求转发给另一台计算机上运行的指定对象,客户可以很容易地获得这种代理对象。企业JavaBeans(EJB)规范是业务新兴的一个重要标准,它的基础之一便是RMI;要理解EJB,必须先了解RMI。无论业界标准如何发展,我们可以预见未来的分布计算仍将离不开代理模式。RMI为代理模式的实践提供了一个很好的例子。

   下面我们简单介绍一下它的实现方式:

 

首先我们的接口必须要继承Remote接口才能够被远程接收的到同时还要扩展UnicastRemoteObject类。

我们的接口定义如下:

 

/**
 * 远程代理接口
 * @author Administrator
 *
 */
public interface ProxyRemote extends Remote {
	public String getName() throws RemoteException;
}

 

 

接口实现如下:

 

/**
 * 远程代理实现
 * @author Administrator
 *
 */
public class RemoteImpl extends UnicastRemoteObject implements ProxyRemote{
	private String name;
	public RemoteImpl(String name) throws RemoteException{
		this.name = name;
	}
	public String getName() {
		return "remote name is:"+name;
	}	
}

 

RemoteImpl 运行在一台机器上,为了能够让其它机子访问它,我们需要为RemoteImpl 对象提供一个代理对象。这个代理对象必须实现 Remote接口,并且提供用于与远程对象通信的附加特性。RMI的最大便利之一就是它能够自动的创建这个代理类。为了自动生成代理类,我们必须把RemoteImpl.java 与ProxyRemote.java 接口文件放在RMI注册的运行目录下.


 >javac packagename.RemoteImpl.java

 >rmic packagename.RemoteImpl                         //RMI编译器,生成用于简化通信的存根

 

rmic命令编译了它所需要的所有类,并创建 了RemoteImpl_Stub类。

 

在对象能够被访问之前,我们必须运行在服务器的上的RIM注册程序注册该对象。

>rmiregistry 5000

 

在端口5000进行监听,注册完之后我们可以开始创建注册RemoteImpl对象.

/**
 * 注册远程代理
 * @author Administrator
 *
 */
public class RegisterRemote {
	public static void main(String[] args) {
		try {
			RemoteImpl remoteImpl = new RemoteImpl("it's a good test!");
			Naming.rebind("rmi://localhost:5000/Remote", remoteImpl);
			System.out.println("register remoteImpl ok");
		} catch (RemoteException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

 

对象被注册完成之后,我们可以通过其他机子访问并得到这个对象,这里本地我们就用localhost来访问并获得这个对象

/**
 * 接收远程代理 
 * @author Administrator
 *
 */
public class ShowRegisterClient {
	public static void main(String[] args) {
		Object obj;
		try {
			obj = java.rmi.Naming.lookup("rmi://localhost:5000/Remote");
			ProxyRemote proxyRemote = (ProxyRemote)obj;
			System.out.println("the result is:"+proxyRemote.getName());
		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (RemoteException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NotBoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		
	}
}

 

当这个程序运行的时候,它首先会通过注册名Remote来查找远程对象。该远程对象代表着远程的一个RemoteImpl对象;lookup返回的对象是RemoteImpl_Stub类的一个实例,RemoteImpl_Stub实现了接口ProxyRemote接口,因此我们可以将这个强制转换为ProxyRemote接口的一个对象,从而可以调用它获得数据。

 

最后结果,屏幕上将会打印如下信息:

the result is:remote name is:it's a good test!

  

 

远程对象代理总结:

   RMI优点在于它使得客户端程序只需要与本地代理对象进行交互便可达到与远程对象通信的目的,RMI用户定义了客户端与服务器端共享对象的接口。RMI为客户端和服务器端分别提供一个ProxyRemote接口的实现类;这两个实现类相协作,从机时可完成进程间的无缝通信。服务器端和客户端则不必关心这些细节。

 

 

 

3)动态代理

 

    我们可能借助动态代理,使用代理对象来包装其他对象,使用代理对象截获对被包装对象的调用请求,然后代理继续把这些请求转发给被包装对象。在执行被截获的调用之前或者之后,我们可以编写相关的加强代码。

    动态代理需要使用对象的类所实现的接口,代理对象可以截获的调用就是这些接口定义的调用。也就是说如果某个类可以实现要截获的接口,动态代理就可以包装这个类的实例。  

  为了创建动态代理,必须具有要截获的的接口列表,如用如下代理就可以获取这个列表:

Class[] classes = obj.getClass().getInterfaces();

 

借助于上述代码,我们可以获得希望截获的方法列表,这些方法属于对象的类实现接口。为构建动态代理,还需要:类加载器,以及包含当代理捕获某些调用时你希望执行的行为的类。对于接口列表,通过使用与希望包装的对象相关的对象,就可以获合适的类加载器。

ClassLoader loader = obj.getClass().getClassLoader();

 

最后一个就是代理对象本身,这个对象必须是实现java.lang.reflect包中的InvocationHandler接口的类实例,接口声明如下:

 

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

 

在动态代理中馐某对象时,对被包装对象的调用会转发给动态代理对象的invoke(),invoke()方法会继续把这个调用转发给被包装对象,可以使用如下进行转发调用:

 

result = m.invoke(obj,args);

 

 

 下面举出一个例子添加一些对象所需要的时候,如下在添加这个对象花费太多时间时就把这个对象打印出来.

public class BadPro {
	private String name;

	public BadPro(String name) {
		this.name = name;
	}

	@Override
	public int hashCode() {
		// TODO Auto-generated method stub
		try {
			Thread.sleep(20);
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}
		return super.hashCode();
	}
	public String toString(){
		return name;
	}
}

 

 

      

public class GoodPro {
	private String name;

	public GoodPro(String name) {
		this.name = name;
	}

	@Override
	public int hashCode() {
		return super.hashCode();
	}
	public String toString(){
		return name;
	}
}

 

 

 

/**
 * 动态代理
 * @author Administrator
 *
 */
public class ImpatientProxy implements InvocationHandler {
	private Object obj;
	private ImpatientProxy(Object obj){
		this.obj = obj;
	}
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		Object result;
		long t1 = System.currentTimeMillis();
		result = method.invoke(obj, args);
		long t2 = System.currentTimeMillis();
		if(t2-t1>10){
		  System.out.println(">It takes"+(t2-t1)+" millions to invoke with:"+method.getName());
		  for(int i =0;i<args.length;i++){
			  System.out.println("> arg["+i+"]"+"   name:"+args[i]+"   class name:"+args[i].getClass().getName());
		  }
		}
		return result;
	}
	public static Object newInstance(Object obj){
		ClassLoader loader = obj.getClass().getClassLoader();
		Class[] classes = obj.getClass().getInterfaces();
		return Proxy.newProxyInstance(loader, classes, 
				new ImpatientProxy(obj));
		
	}
	public static void main(String[] args) {
		Set s = new HashSet();
		s = (Set)ImpatientProxy.newInstance(s);
		s.add(new BadPro("BadPro"));
		s.add(new GoodPro("GoodPro"));
		s.add(new Object());
		System.out.println("the set contains :"+s.size()+" things.");
	}

}

 

这个类实现invoke方法,以检查被包装对象执行某调用操作所花费的时间太长时,会打印报警信息。

为保证ImpatientProxy对象有效,需要使用Proxy类来简化动态代理的创建过程,所以我们加入了newInstance方法:

	public static Object newInstance(Object obj){
		ClassLoader loader = obj.getClass().getClassLoader();
		Class[] classes = obj.getClass().getInterfaces();
		return Proxy.newProxyInstance(loader, classes, 
				new ImpatientProxy(obj));
		
	}

  能够在被截获方法调用执行前和执行后创建其他有意义的操作是面向方面编辑(AOP)的念之一。在AOP中,方法aspect就是建议和切入点的组合。

在JAVA中,动态代理技术使你能够使用代理对象包装其他对象,截获被包装对象的调用,在调用传递前后增加其他操作等,这样可以比较随意地给任何对象增加可复用的行为,从这点来讲,与AOP非常类似。

 

 

小结:

   代理模式的实现要求建立一个占位对象,用于控制对目标对象的访问, 这样客户端就无需了解目标对象的状态变化。就像加载一个图像需要一定时间时,我们可能使用代理模式改善用户体验。但代理模式本身存在代理对象与被代理对象之间的偶合程序过紧的问题。在JAVA中动态代理有进可以提供一种增加可复用功能的机制。如果某对象的类可实现要截获的接口,可以使用动态代理包装该对象,增加自己的处理逻辑,以增加或者替换被包装对象代码的功能。

 

 

参与资料:Java设计模式

               [美] Steven John Metsker

                      William C.Wake

  • 大小: 12.9 KB
分享到:
评论
1 楼 EyejavaLi 2010-03-04  

相关推荐

    21种设计模式略见一斑

    - 代理模式:为其他对象提供一种代理以控制对这个对象的访问。 - 模板方法模式:定义一个操作中的算法骨架,而将一些步骤延迟到子类中。 3. **行为型模式**: - 责任链模式:避免向多对象发送请求,而是将请求...

    设计模式之略见一斑(Observer观察者模式)

    在软件开发中,设计模式是解决常见问题的模板,它们为程序员提供了一种标准化的解决方案,使得代码更易于理解和维护。本篇文章将深入探讨“Observer”观察者模式,这是一种行为设计模式,它定义了对象之间的一对多...

    CRC16或32校验

    CRC的全称为Cyclic Redundancy Check,中文名称为循环冗余校验。它是一类重要的线性分组码,编码...例如我们读软盘上的文件,以及解压一个ZIP文件时,偶尔会碰到“Bad CRC”错误,由此它在数据存储方面的应用可略见一斑

    成语大全带解释.doc

    10. **略见一斑**:形容从局部可以推测到整体的一部分情况。 11. **有口皆碑**:形容人们对某人或某事的赞扬之声普遍且一致。 12. **并行不悖**:两个或多个事情可以同时进行,彼此之间并不冲突。 13. **白璧微瑕**...

    2021-2025年中国避雷器行业调研及竞合策略研究报告.pdf

    报告的核心内容可从提供的“部分内容”中略见一斑,以下是对报告内容的详细解读: 一、企业竞合策略概述 报告首先介绍了避雷器行业竞合策略研究的背景、研究原则与方法,并阐述了研究企业竞合策略的重要性和实践...

    高一语文上册第二单元综合测试1[精选].doc

    1. 语文基础知识:题目涉及到词语辨析,如“心酸”与“辛酸往事”,“一般”与“略见一斑”,“过度”与“过渡时期”,“无谓”与“无私无畏”,“家世”与“处理家事”,“明智”与“淡泊明志”,“灌注”与...

    小学生易错字及生僻字总结.doc

    6. 略见一斑:从一点细微之处就能看出整体的情况或品质。 7. 班门弄斧:在专家面前卖弄自己的技能,暗示自不量力。 8. 英雄辈出:形容一个时代或群体中不断涌现出杰出的人物。 9. 并行不悖:两件事物同时进行而互不...

    280个容易读错、写错、用错的成语要点.doc

    12. "略见一斑"(lüè jiàn yī bān):从部分看出整体,通过局部了解全局。 13. "暴虎冯河"(bào hǔ píng hé):形容有勇无谋,冒险行事。 14. "有口皆碑"(yǒukǒu jiē bēi):形容人人称赞,具有很高的...

    现场活动大屏幕系统v1.1806221.zip

    《现场活动大屏幕系统v1.1806221》是一款专为现场活动设计的多媒体展示系统,它的核心功能在于提供一个高效、稳定且易于操作的大屏幕展示平台。这款系统能够帮助活动组织者在活动现场实时展示各种信息,如活动进程、...

    怀念母亲练习题.docx

    - (3) "我当时的想法,从这几段文字中也可以看出一点" 可以替换为 "言外之意,略见一斑"。 2. 动词填空: - ① "暗夜渐渐__上天空,__上对面的屋顶" 可以填入 "爬上,爬满",表达夜晚降临的情景。"一切都__在朦胧...

    小学语文成语大全及其解释.pdf

    10. **略见一斑**:从局部看出事物的一部分特征,了解其大概。 11. **有口皆碑**:所有人都称赞,形容事迹或人品受到普遍赞扬。 12. **并行不悖**:两件事情可以同时进行,互不冲突。 13. **白璧微瑕**:完美的事物...

    容易用错的成语一览表.doc

    4. **黯然失色**:形容相比之下显得差远了,不如别人。同义词有“相形见绌”、“大相径庭”。反义成语如“光彩夺目”、“不相上下”。 5. **慷慨激昂**:形容精神振奋,情绪高昂,充满正气。同义词有“热血沸腾”、...

    《争吵》课件.ppt

    《爱的教育》的影响力之广泛,从它的翻译版本就能略见一斑。作为一部文学作品,它跨越国界和文化,成为全人类的精神财富。通过阅读《争吵》,学生不仅能够提升自己的语言表达能力,更能在情感和道德上得到深刻的教育...

    曲周事业单位招聘2018年考试真题及答案解析考试版(1).docx

    "略见一斑"意味着事情的端倪已经显现,“光明灿烂”和“辉煌”均形容美好前景,但前者更侧重于未来的可能性,后者侧重于实际的辉煌成就。因此,选B:希望光明,光明璀璨。 8. 经济形势描述:第八题中,“内热外冷”...

    小学语文成语大全及其解释.doc

    10. **略见一斑**:通过部分了解整体,形容对事物的初步认识。 11. **有口皆碑**:所有人都称赞,表示对某人或某事的普遍好评。 12. **并行不悖**:两件事情同时进行,并不互相冲突。 13. **白璧微瑕**:比喻美好的...

    高一语文洞察世道沧桑测试[精选].doc

    - 《红楼梦》是中国古代四大名著之一,属于章回体小说,现有120回,前80回由曹雪芹创作,后40回一般认为是高鹗续写。曹雪芹,原名霑,字梦阮。《红楼梦》以贾、史、王、薛四大家族的兴衰为背景,以贾宝玉和林黛玉的...

    CRC算法与实现.doc

    例如,我们读软盘上的文件,以及解压一个 ZIP 文件时,偶尔会碰到“Bad CRC”错误,由此它在数据存储方面的应用可略见一斑。 CRC 的优点 CRC 的优点在于编码和解码方法简单,检错和纠错能力强,在通信领域广泛地...

    年高一年级语文下册第二次段考试卷.doc

    题目2考察学生对汉字书写的准确性,如"烦燥"应为"烦躁"、"甜言密语"应为"甜言蜜语"、"颓垣"、"略见一班"应为"略见一斑"、"莫钟一是一言既出,驷马难追"中"莫钟一是"可能是错误,正确可能是"莫衷一是"。 3. 成语运用...

    2015_2016学年高中语文课时训练10雷雨鲁人版必修4

    3. 字形校正:题目中还包含了错别字的纠正,如A项中的“烦燥”应为“烦躁”,“甜言密语”应为“甜言蜜语”,B项中的“半响”应为“半晌”,“略见一班”应为“略见一斑”,C项中的“脉膊”应为“脉搏”,“歪风斜气...

Global site tag (gtag.js) - Google Analytics