博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【设计模式】学习笔记16:代理模式之虚拟代理(实现CD封面加载器)
阅读量:4073 次
发布时间:2019-05-25

本文共 5656 字,大约阅读时间需要 18 分钟。

本文出自   

在上篇中,我们学习了,并用Java RMI实现了一个最简单的远程代理。
实际上代理模式并不仅仅应用与远程代理,还有很多其他的应用。
比如:虚拟代理。
代理模式可以以很多形式出现,但基本上符合一般代理的设计。为何有这么多的形式呢?因为代理模式可以被用在许多不同的例子中。

走进代理模式

我们打算建立一个应用程序,用来展现你最喜欢的CD封面。你可以建立一个CD标题菜单,然后从豆瓣等网站的在线服务中取得CD封面的图。如果使用Swing,可以创建一个Icon接口从网络上加载图像。唯一的问题是,限于连接带宽和网络负载,下载可能需要一些时间,所以在等待图片加载的时候,应该显示一些东西。我们希望在等待图像时整个应用程序被挂起。一旦图像被加载完成,刚才显示的东西应该消失,图像显示出来。
效果图:
正在下载中:
下载完成并且显示:
想要做到这样,简单的方式就是利用虚拟代理。虚拟代理可以代理Icon,管理背景的加载,并在加载未完成时显示“CD封面加载中,请稍后......”,一旦加载完成,代理就把显示的职责委托给Icon
比较远程代理和虚拟代理:
远程代理:远程代理可以作为另一个JVM上对象的本地代表。调用代理的方法,会被代理利用网络转发到远程执行,并且结果会通过网络返回给代理,再由代理将结果转给客户。
虚拟代理:虚拟代理是作为创建开销大的对象的代表。虚拟代理经常直到我们真正需要一个对象的时候才创建它。当对象在创建前和创建中时,由虚拟代理来扮演对象的替身。对象创建后,代理就会将请求直接委托给对象。

设计CD封面虚拟代理

此类图和远程代理的图很类似,但是这里的代理是用于隐藏创建开销大的对象(因为我们要通过网络取得图像数据),而不是隐藏
在网络其他地方的对象
ImageProxy如何工作:
1. ImageProxy首先创建一个ImageIcon,然后开始从网络URL上加载图像
2. 在加载的过程中,ImageProxy显示”CD封面加载中,请稍后......“
3. 当图像加载完毕,ImageProxy把所有方法调用委托给真正的ImageIcon,这些方法包括了paintIcon(), getWidth()和getHeight().
4. 如果用户请求新的图像,我们就创建新的代理,重复这样的过程。

使用虚拟代理实现CD封面加载器

1. 编写ImageProxy
package com.zsd;import java.awt.Component;import java.awt.Graphics;import java.net.URL;import javax.swing.Icon;import javax.swing.ImageIcon;//实现Icon接口public class ImageProxy implements Icon {	ImageIcon imageIcon;	URL imageURL;	Thread retrievalThread;	boolean retrieving = false;	// 将图片的URL传入构造器中	public ImageProxy(URL url) {		imageURL = url;	}	// 在图像加载完毕前,返回默认的宽和高	// 图像加载完毕后,装给iamgeIcon处理	public int getIconWidth() {		if (imageIcon != null) {			return imageIcon.getIconWidth();		} else {			return 800;		}	}		public int getIconHeight() {		if (imageIcon != null) {			return imageIcon.getIconHeight();		} else {			return 600;		}	}	// 当要在屏幕上绘制图像时,就调用此方法	public void paintIcon(final Component c, Graphics g, int x, int y) {		// 如果已经有了icon,就画出		if (imageIcon != null) {			imageIcon.paintIcon(c, g, x, y);		} else {			// 还没有icon时,就显示“加载中...”的消息			g.drawString("CD封面加载中,请稍后...", x + 300, y + 190);			if (!retrieving) {				retrieving = true;								// 在这个线程中加载真正的icon图像。注意,加载图像和ImageIcon是同步(synchronous)				// 也就是说,只有在加载完之后,ImageIcon构造器才会返回。这样,我们的程序会耗在这里				// 所以要把加载变成异步(asynchronous)的。				retrievalThread = new Thread(new Runnable() {					public void run() {						try {							imageIcon = new ImageIcon(imageURL, "CD Cover");							c.repaint();						} catch (Exception e) {							e.printStackTrace();						}					}				});				retrievalThread.start();			}		}	}}
2. ImageComponent是放在frame布局中用来显示图片的,会把下载的图片传进去并显示出来
package com.zsd;import java.awt.*;import javax.swing.*;class ImageComponent extends JComponent {	private Icon icon;	public ImageComponent(Icon icon) {		this.icon = icon;	}	public void setIcon(Icon icon) {		this.icon = icon;	}	public void paintComponent(Graphics g) {		super.paintComponent(g);		int w = icon.getIconWidth();		int h = icon.getIconHeight();		int x = (800 - w)/2;		int y = (600 - h)/2;		icon.paintIcon(this, g, x, y);	}}
3. 测试CD封面浏览器
package com.zsd;import java.net.*;import java.awt.*;import java.awt.event.*;import javax.swing.*;import java.util.*;public class ImageProxyTestDrive {		ImageComponent imageComponent;	JFrame frame = new JFrame("CD封面加载器");    JMenuBar menuBar;  // 菜单栏    JMenu menu;   	   // 菜单	Hashtable cds = new Hashtable(); 	public static void main (String[] args) throws Exception {		ImageProxyTestDrive testDrive = new ImageProxyTestDrive();	} 	public ImageProxyTestDrive() throws Exception{				// 构造菜单项用的, key=CD名,  value=URL		cds.put("三国志:英雄的黎明","http://img3.douban.com/lpic/s4131026.jpg");        cds.put("东邪西毒","http://img3.douban.com/lpic/s10425517.jpg");        cds.put("龙猫","http://img3.douban.com/lpic/s1668213.jpg");        cds.put("Once
<曾经>
","http://img3.douban.com/lpic/s2821080.jpg"); cds.put("太阳照常升起","http://img3.douban.com/lpic/s4714977.jpg"); cds.put("蝙蝠侠:侠影之谜","http://img3.douban.com/lpic/s4591642.jpg"); cds.put("天国王朝","http://img3.douban.com/lpic/s2595263.jpg"); // 设置初始的CD封面 URL initialURL = new URL((String)cds.get("Once
<曾经>
")); // 建立菜单栏 menuBar = new JMenuBar(); menu = new JMenu("最爱的CD"); menuBar.add(menu); frame.setJMenuBar(menuBar); for(Enumeration e = cds.keys(); e.hasMoreElements();) { String name = (String)e.nextElement(); JMenuItem menuItem = new JMenuItem(name); menu.add(menuItem); menuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { imageComponent.setIcon(new ImageProxy(getCDUrl(event.getActionCommand()))); frame.repaint(); } }); } // set up frame and menus Icon icon = new ImageProxy(initialURL); imageComponent = new ImageComponent(icon); frame.getContentPane().add(imageComponent); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(800,600); frame.setVisible(true); } URL getCDUrl(String name) { try { return new URL((String)cds.get(name)); } catch (MalformedURLException e) { e.printStackTrace(); return null; } }}
OK了,上面就是全部代码了,编译后便可以运行了。
让我们来回顾一下,运行这个程序发生了什么?
1. 我们会创建了一个用来显示的ImageProxy代理。paintIcon()方法会被调用,而ImageProxy会产生线程取得图像,并创建ImageIcon.
2. 在某个时间点,也就是图片下载完后,图像被返回,ImageIcon被完整实例化。
3. 在ImageIcon被创建后,下次调用到paintIcon()时,代理就委托ImageIcon进行。

小结

现在再来回顾虚拟代理的定义,虚拟代理为什么取名为“虚拟”代理呢?以这个CD封面下载器为例,CD浏览器真正需要的是一个ImageIcon也就是封面图片并要让它显示出来,但是由于图片还在网上不存在本地,所以就用了一个代理ImageProxy来假装就是“ImageIcon”,对于CD浏览器来说,他把这个代理看作和一般的图片一样,让他显示出来。但是刚创建代理Imageproxy时,这个代理根本就没有图片!所以说他是“虚拟”的,挂了个马甲装自己就是图片!一直到下载完图片之后,才真正地把图片显示出来。

远程代理和虚拟代理看起来好下给你差别很大,但是他们有共同点:都会对主题(subject)施加的方法拦截下来。这种间接的级别让我们可以做许多事,包括将请求分发到远程主题;给创建开销大的对象对象提供代表;或者提供某些级别的保护,这种保护能决定哪些客户能调用哪些方法。
一个缺点:
在这个例子中,每次要换一个CD封面图片,都要创建新的ImageProxy来去的图像,即使图像已经取回来过了。可以使用缓存代理(Caching Proxy),缓存代理会维护之前创建的对象,当收到请求时,在可能的情况下返回缓存对象。
你可能感兴趣的文章
Socket服务器整体架构概述
查看>>
有了WCF,Socket是否已人老珠黄?
查看>>
浅析 c# Queue
查看>>
c# 温故而知新: 线程篇(一)
查看>>
Python Twisted 框架中 socket通信
查看>>
TCPIP高效编程-改善网络程序的44个技巧[PDF]
查看>>
Socket经验记录
查看>>
SOCKET是多线程安全的吗? [问题点数:40分,结帖人CSDN]
查看>>
<base href=""/> 的应用
查看>>
是否可以在不同线程中不加任何同步的往某个 socket 上写?
查看>>
线程已被中止- “Thread was being aborted”
查看>>
AS3.0 正则表达式规则
查看>>
更新Svn客户端后,右键菜单中没有TortoiseSVN了
查看>>
Flash剪贴板功能
查看>>
FlashInspector 【Firefox浏览器插件,flash分析工具】
查看>>
xampp 用phpmyadmin在页面上修改密码后,无法登陆,密码没问题
查看>>
十个Flex/Air疑难杂症及解决方案简略
查看>>
Vector3D - AS3
查看>>
球面的纹理映射
查看>>
Flash Builder 找不到所需的 Adobe Flash Player
查看>>