《Head First Design Patterns》:第11章,代理(Proxy)模式之“虚拟代理”

定义

虚拟代理作为创建开销大的对象的代表。虚拟代理经常直到我们真正需要一个对象的时候才创建它。当对象在创建前和创建中时,由虚拟代理来扮演对象的替身。对象创建后,代理就会将请求直接委托给对象。

场景

可以显示CD封面的Swing应用

场景介绍

建立一个应用程序,用来展现你最喜欢的CD封面。应用程序提供一个下拉菜单,然后从相应网站取得对应的CD封面。已知,限于连接带宽和网络负载,下载可能需要一些时间,所以在等待图像加载的时候,应该给出一些友好的提示。要求在等待图像时整个应用程序不要被挂起。一旦图像加载完成,Swing组件上显示相应的图像。

解决方案

ImageProxy代理用于隐藏创建开销大的对象(因为我们需要通过网络获取图像数据)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import java.net.*;
import java.awt.*;
import javax.swing.*;

class ImageProxy implements Icon {
volatile ImageIcon imageIcon;
final URL imageURL;
Thread retrievalThread;
boolean retrieving = false;

public ImageProxy(URL url) { imageURL = url; }

public int getIconWidth() {
if (imageIcon != null) {
return imageIcon.getIconWidth();
} else {
return 800;
}
}

public int getIconHeight() {
if (imageIcon != null) {
return imageIcon.getIconHeight();
} else {
return 600;
}
}

synchronized void setImageIcon(ImageIcon imageIcon) {
this.imageIcon = imageIcon;
}

public void paintIcon(final Component c, Graphics g, int x, int y) {
if (imageIcon != null) {
imageIcon.paintIcon(c, g, x, y);
} else {
g.drawString("Loading album cover, please wait...", x+300, y+190);
if (!retrieving) {
retrieving = true;

retrievalThread = new Thread(new Runnable() {
public void run() {
try {
setImageIcon(new ImageIcon(imageURL, "Album Cover"));
c.repaint();
} catch (Exception e) {
e.printStackTrace();
}
}
});

retrievalThread.start();
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import java.awt.*;
import javax.swing.*;

class ImageComponent extends JComponent {
private static final long serialVersionUID = 1L;
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);
}
}