【设计模式】学习笔记16:代理模式之虚拟代理(实现CD封面加载器)

article/2025/8/19 20:31:14



本文出自   http://blog.csdn.net/shuangde800




在上篇中,我们学习了代理模式,并用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=URLcds.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 menusIcon 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),缓存代理会维护之前创建的对象,当收到请求时,在可能的情况下返回缓存对象。























http://chatgpt.dhexx.cn/article/BTnaRwy3.shtml

相关文章

代理模式(三):远程代理,虚拟代理,缓冲代理

15.4 远程代理 远程代理(Remote Proxy)是一种常用的代理模式&#xff0c;它使得客户端程序可以访问在远程主机上的对象&#xff0c;远程主机可能具有更好的计算性能与处理速度&#xff0c;可以快速响应并处理客户端的请求。远程代理可以将网络的细节隐藏起来&#xff0c;使得客…

虚拟机 全局代理 主机代理_比较虚拟代理与真实代理的性能

云计算的关键基础是虚拟化。 面向云的设计人员&#xff0c;开发人员和管理员需要问自己的一个问题是&#xff1a;“虚拟化组件的性能水平如何与其“真实”物理对应物相提并论&#xff1f;” “如果存在负面差距&#xff0c;我该如何克服呢&#xff1f;” 本文介绍了在虚拟机&a…

设计模式——代理模式(虚拟代理)

代理模式的类型分为: (1)虚拟代理 (2)远程代理 (3)智能指引 (4)保护代理 这一篇主要讲虚拟代理,想要知道其他类型讲解的小伙伴可以去我其他博客翻一翻哦。 首先来理解一波虚拟代理,啥叫虚拟代理? 举个很常见也很通俗的例子,咱们平时抽奖的时候,是不是都想要抽个…

[转载]虚拟代理模式(Virtualnbsp;Proxy)

虚拟代理模式(Virtualnbsp;Proxy) 第25章 虚拟代理模式(Virtual Proxy) 描述&#xff1a; 虚拟代理模式 (Virtual Proxy)是一种节省内存的技术&#xff0c;它建议创建那些占用大量内存或处理复杂的对象时&#xff0c;把创建这类对象推迟到使用它的时候。在特定的应用 中&#x…

代理模式——虚拟代理(二)

代理模式定义 为另一个对象提供一个替身或占位符以控制对这个对象的访问。使用代理模式创建代表对象&#xff0c;让代表对象控制对某对象的访问&#xff0c;被代理的对象可是远程的对象、创建开销大的对象或需要安全控制的对象。 代理分三种&#xff1a; 1.远程代理&#xff…

SQL语法与数据库快速入门(1)

目录 数据库简介数据库分类常用数据库简介使用场景MySql 的安装与配置数据库客户端工具MySql 介绍SQL 简介DDL 数据库操作-创建DDL 数据库操作-查看DDL 数据库操作-修改DDL 数据库操作-删除DDL 数据库表操作简介DDL 数据库表操作-创建DDL 数据库表操作-查看DDL 数据库表操作-修…

SQL语法与数据库快速入门(2)

目录&#xff1a; 多表简介SQL 约束-外键约束多表关系简介多表查询多表查询-内连接查询多表查询-外连接查询子查询简介子查询实战数据库进阶redis 内存数据库mongodb nosql 数据库neo4j 图数据库 1.多表简介 多表及使用场景介绍&#xff1a; 多表顾名思义就是在数据库设计中…

【数据库】MYSQL轻松入门

文章目录 MYSQL入门一、MYSQL概述1. 数据库相关概念1.1 数据库&#xff0c;数据库管理系统与SQL1.2 数据库种类以及主流数据库管理系统排名1.2.1 数据库的种类1.2.2 数据库主流排名 1.3 MySQL数据库安装1.3.1 下载1.3.2 修改密码 1.4 数据模型 二、SQL2.1 通用语法与注释2.2 SQ…

Java连接mysql数据库的五分钟快速入门教程

总体流程&#xff1a;数据库->Java 总体步骤&#xff1a; 1.创建数据库并新建表 2.创建一个Java项目&#xff0c;在项目下新建文件夹lib&#xff0c;类型为Directory 3.将下载好mysql-connector-java-8.0.27.jar放到lib目录下 4.将lib下的依赖添加到Java项目中 5.编写代码连…

数据库快速入门教程--视频

数据库快速入门教程--视频 下载地址&#xff1a;http://v.51work6.com/courseInfoRedirect.do?actioncourseInfo&courseId240579本课程是这个课程体系的核心之一&#xff0c;为软件开发人员所需数据库知识的学习教材&#xff0c;而不是培训一个DBA&#xff08;数据库管理员…

Docker 安装与操作 Mysql 数据库快速入门

目录 演示环境说明 Mysql 镜像检索 Mysql 镜像下载 Mysql 容器运行 错误运行方式 正确运行方式 端口映射 连接测试 高级命令指定编码运行 演示环境说明 1、《 Docker 镜像操作 常用命令》中说明了如何从 Docker Hub 上下载镜像&#xff0c;《 Docker 容器操作 常用命…

数据库(MySQL)基础快速入门!

前言&#xff1a;这篇送给那些想要学会数据库&#xff0c;但又不想花太多时间的同学们。看了这篇数据库知识&#xff0c;轻轻松松搞定它&#xff01;用不同的工具操作数据库命令可能会有小小的不同&#xff0c;我用的是系统自带的cmd。&#xff08;凡是数据库名称&#xff0c;数…

MySQL数据库1——数据库快速入门

MySQL数据库——数据库基础 &#xff08;一&#xff09;为什么使用数据库 数据库用来存储海量数据数据库利于数据查询和管理数据库相对于文件安全性更高数据库相对于文件利于控制 &#xff08;二&#xff09;MySQL的基本使用 安装好MySQL数据库以后&#xff0c;连接服务器。…

mysql快捷创建数据库_mysql数据库快速入门(1)

1、数据库操作 1.1、连接mysql服务器 mysql -u root( 用户名 ) -p 1.2、退出mysql命令提示窗 exit 1.3、查看版本 SELECT VERSION(); 1.4、列出数据库列表 SHOW DATABASES; 1.5、创建数据库 CREATE DATABASE IF NOT EXISTS [database] DEFAULT CHARSET UTF8; 1.6、选择要操作的…

SQLite3数据库的快速入门菜鸟教程

SQLite3是一个轻量级嵌入式数据库&#xff0c;其主要有以下的特点&#xff1a; 1、零配置无需安装和管理配置&#xff1b; 2、储存在单一磁盘文件中&#xff1b; 3、数据库文件可在不同字节序的机器自由共享&#xff1b; 4、比目前流行的大多数数据库对数据的操作要快&…

MySQL数据库快速入门

MySQL基础 1、数据库相关概念 以前我们做系统&#xff0c;数据持久化的存储采用的是文件存储。存储到文件中可以达到系统关闭数据不会丢失的效果&#xff0c;当然文件存储也有它的弊端。假设在文件中存储以下的数据&#xff1a; 姓名 年龄 性别 住址 张三 23 男 北京西三旗…

MySQL数据库快速入门到精通(超详细保姆级,建议收藏)这可能是目前最适合你的教程,从基础语法到实例演示。

前言 此文章旨在为需要掌握快速开发和复习MySQL的同学所准备&#xff0c;您完全可以把此文章当作参考文档来使用&#xff0c;本文将尽量精简&#xff0c;使您快速的理解和掌握语法。 关于MySQL MySQL是一个关系型数据库管理系统&#xff0c;由瑞典MySQL AB公司开发&#xff0c;…

超经典两万字,MySQL数据库快速入门。

「作者主页」&#xff1a;士别三日wyx 「作者简介」&#xff1a;CSDN top100、阿里云博客专家、华为云享专家、网络安全领域优质创作者 「专栏简介」&#xff1a;此文章已录入专栏《MySQL数据库快速入门》 MySQL快速入门 一、环境准备1. MySQL下载及安装2. MySQL启动3. MySQL客…

SQL数据库快速入门基础

SQL(Structure Query Language,结构化查询语言)语言是国际标准化组织(ISO)采纳的标准数据库语言。 数据库就是一幢大楼,我们要先盖楼,然后再招住户(住户当然就是数据库对象,)。我们盖得大楼的基本格局设计师们已经为我们设计好,我们在创建数据库过程中,系统(设计师)…

数据库快速入门

数据库快速入门 目录 数据库快速入门 1. 数据库基础知识入门 2. Postgre数据库介绍 3. 数据库入门操作 3.1常用命令 3.2 数值类型 3.3 数据运算符 4. 常用函数介绍 4.1 常用函数介绍 4.2 自定义函数 5 数据库索引和视图介绍 5.1 数据库索引介绍 5.2 数据库视图介…