代理模式,从起名字我们就可以猜出一二,意思是A要调用对象B,处于某些原因,不便于直接接触,而通过proxy代理类来代为调用。其原因主要可以归为如下几类:
1. B类封装的要求,不便于客户程序了解太多。用来控制真实对象时的访问权限(安全代理)
2. A调用远程对象B,多有不便,因此在本地建造Proxy代为处理,A直接调用Proxy,就像操作本地对象。(远程代理)
3. A在调用B前和B后要做一些必要的其他工作,使用代理类就可以把这些功能封装在一起。(职能指引)
4. B构建和初始化费时费力,创建虚拟代理存放其实例。(虚拟代理)
代理模式的简单类图表示如下:
在上图中,代理类和真实类都继承至同一接口,并且代理类中存在真实类的实例。
在java多线程中, Thread t = new Thread(new Runnable(){public void run(){}}); 则是典型的代理模式。 这种设计的目的使得创建多线程的方式便的更加灵活。与上面代理模式的典型类图有所差异, Runnable为接口,Thread继承至Runnable。
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
group.add(this);
start0();
if (stopBeforeStart) {
stop0(throwableFromStop);
}
}
private native void start0();
public void run() {
if (target != null) {
target.run();
}
}
从代码中可以看到,通过start()启动线程后,在start()中会调用本地方法start0(), 该方法会在底层完成线程初始化工作并最终调用run()启动线程, 在run()中则调用了构造方法中传递的Runnable对象。
这里再举一个因为B对象加载缓慢(即虚拟代理)而使用代理的例子。例子来源于《Java与模式》,自己感觉这本书虽然没有太大价值,但是这个例子还是能说明一些问题的。
package learn.sync;
import java.awt.Graphics;
import java.awt.Insets;
import javax.swing.Icon;
import javax.swing.JFrame;
public class Client extends JFrame{
private static int WIDTH = 270;
private static int HEIGHT = 380;
private Icon imageIcon = null;
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Client client = new Client();
client.setVisible(true);
}
public Client(){
super("proxy example");
imageIcon = new ImageIconProxy("E:\\xx.jpg",WIDTH,HEIGHT);
this.setBounds(100, 100, WIDTH+10, HEIGHT+10);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void paint(Graphics g)
{
super.paint(g);
Insets inset = this.getInsets();
imageIcon.paintIcon(this, g,inset.left , inset.top);
}
}
package learn.sync;
import java.awt.Component;
import java.awt.Graphics;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.SwingUtilities;
public class ImageIconProxy implements Icon {
private ImageIcon icon;
private String path;
private int width;
private int height;
private boolean isFinish = false;
public ImageIconProxy(String path, int width, int height){
this.path = path;
this.width = width;
this.height = height;
}
@Override
public int getIconHeight() {
// TODO Auto-generated method stub
return icon.getIconHeight();
}
@Override
public int getIconWidth() {
// TODO Auto-generated method stub
return icon.getIconWidth();
}
@Override
public void paintIcon(final Component c, Graphics g, int x, int y) {
// TODO Auto-generated method stub
if(isFinish){
icon.paintIcon(c, g, x, y);
}
else
{
g.drawRect(x, y, width-1, height-1);
g.drawString("loading", x+10, y+10);
/*
synchronized(this){
SwingUtilities.invokeLater(new Runnable(){
public void run(){
try {
Thread.currentThread().sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
icon = new ImageIcon(path);
isFinish = true;
c.repaint();
}
});
}*/
Thread t = new Thread(){
public void run(){
try {
Thread.currentThread().sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
icon = new ImageIcon(path);
isFinish = true;
c.repaint();
}
};
t.setDaemon(true);
t.start();
}
}
其中,上面代码的注释部分是原书中的代码,认为SwingUtilities.invokerLater()是开启独立的新线程,本人觉得是完全错误的。 该方法是加入事件派发线程的,swing是单线程的图形API,读者如果不了解这一部分,需要认真看一下,许多人都没能正确理解。 这种与界面无关的耗时操作应该放在新的线程中,不然会阻塞当前窗口,使得窗口不能响应事件,所以这里我又建了一个新线程。
这里,我们可以清楚的看到代理类为ImageIconProxy ,而实际类则是ImageIcon icon,作为成员存在于ImageIconProxy。代理类完成缓冲提示,加载,最终调用ImageIcon的paintIcon方法绘制图片。