分享
 
 
 

大卫的Design Patterns学习笔记12:Proxy

王朝other·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

一、概述

大家都用过代理服务器,代理服务器是从出发点到目的地之间的中间层。而Proxy模式中的Proxy功能上与此类似,是对象的访问者与对象之间的中间层。

Proxy(代理)模式可用于解决在直接访问对象不方便或不符合要求时,为这个对象提供一种代理,以控制对该对象的访问。

二、结构

Proxy模式的类图结构如下图所示:

图1:Proxy模式类图示意

在上面的类图中,Proxy类是Subject类的子类,但个人认为,单纯从Proxy的意图上讲,这一约束不是必须的。

与前面讨论的Decorator模式不同,在应用Proxy模式时,我们可能知道目标,也可能不知道目标(此时,被代理的对象由Proxy类创建),而Decorator模式下我们往往按照访问目标的方式去访问Decorator,即我们总是知道目标,或者说我们更注意的是目标,而不是Decorator。并且,我们Proxy类可能提供与被访问者不同的接口,而Decorator模式下应保证接口的一致性,以便用户可以用与访问Decoratee一样的方式来访问Decorator。

单纯从结构上讲,Adapter模式与Proxy模式比较相似,但二者的区别在于意图的不同:Adapter的意图在于接口的转换,而Proxy的意图在于代理(或控制),因此,有人形象地将Proxy模式称为“票贩子模式”,而将Adapter模式称为“外汇买卖模式”。

三、应用

Proxy这个模式的目的比较笼统,引入Proxy的目的是在Subject和Client之间构建一个中间层,它适用的情况很多(以至于JDK1.3开始,特别添加了对Proxy的支持,详见java.lang.reflect.Proxy说明文档),可以简单分成以下几类:

1、远程访问代理(可能为了简化客户代码,也可能为了集中管理等);

2、重要对象(可能是共享对象或大的,耗资源的对象)访问代理;

3、访问控制代理。

在实际应用中,可能出现同时属于以上多种类别的情况。下面是一些可以应用Proxy模式的常见情况:

1、远程(Remote)代理:为一个位于不同的地址空间的对象提供一个局域代表对象。这个不同的地址空间可以是在本机器中,也可是在另一台机器中,远程代理又叫做大使(Ambassador)。常见的应用如CORBA、DCOM等。

2、虚拟(Virtual)代理:根据需要创建一个资源消耗较大的对象,使得此对象只在需要时才会被真正创建。如某个Word文档中包含很多较大的图片,需要花费很长时间才能显示出来,那么使用编辑器或浏览器打开这个文档,不能等待大图片处理完成,这时需要做个图片Proxy来代替真正的图片,先在图片的位置显示一个框,然后随着图片逐步被打开,再对显示区域以及内容进行调整,直至显示所需内容。再比如说数据库的显示,客户现在只需要显示1-10条,你放在内存中10000条对客户没有任何意义,这时可以用一个代理,只是取出前10条就可以了,当客户选择显示下10条时再取出接下来的10条数据显示给客户。

3、Copy-on-Write代理:虚拟代理的一种。把复制(克隆)拖延到只有在客户端需要时,才真正采取行动。这在访问大的对象和防止频繁拷贝造成过多消耗时经常被用到,现代操作系统往往使用这种技术来防止频繁写磁盘,对于我们的普通应用而言,这种技术也被经常使用,当对象未发生修改时,先使用已有的对象,任何一方发生修改时才执行拷贝动作,创建新的对象,以避免在创建对象上过多的消耗(因为我们有可能在整个对象存在期间不会被修改),从而提高处理效率。

4、保护(Protect or Access)代理:控制对一个对象的访问,如果需要,可以给不同的用户提供不同级别的使用权限。对于硬件等系统资源而言,OS即是一层保护代理。

5、Cache代理:为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果。

6、防火墙(Firewall)代理:保护目标,不让恶意用户接近。

7、同步化(Synchronization)代理:使几个用户能够同时使用一个对象而没有冲突。

8、智能引用(Smart Reference)代理:当一个对象被引用时,提供一些额外的操作,比如将对此对象调用的次数记录下来等。

总之,当需要对Client访问目标对象的行为进行控制时,请尽可能不要通过继承来对目标进行深入规范,而是采用Proxy模式在Client和Subject之间建立一个中间层,这将为我们的系统提供更大的可扩展性。

四、优缺点

五、举例

由于Proxy模式的目的是在Subject和Client之间构建一个中间层,以控制Client对Subject的访问,其应用十分广泛,很难举出一个面面俱到的例子,以下用两个典型应用来简单演示Proxy模式的应用。如果你感兴趣,也可以研究一下STL的auto_ptr和string的实现,其中也用到了Proxy模式,其中,std::string使用了Copy-on-Write技术。

下面是一个采用Proxy模式进行图片加载的例子(Virtual Proxy, Java Code):

注:运行该示例前请在程序运行目录下放置一个图片文件,并将其命名为test.jpg

import java.awt.*;

import java.awt.event.*;

import javax.swing.*;

import javax.swing.border.*;

// This class tests a virtual proxy, which is a proxy that

// delays loading an expensive resource (an icon) until that

// resource is needed.

public class VirtualProxyTest extends JFrame {

private static String IMAGE_NAME = "test.jpg";

private static int IMAGE_X = 256, IMAGE_Y = 256;

private JPanel normPanel, proxyPanel;

public VirtualProxyTest() {

super("Virtual Proxy Test");

normPanel = new NormPanel(IMAGE_NAME);

proxyPanel = new ImageProxy(IMAGE_NAME);

normPanel.setPreferredSize(new Dimension(IMAGE_X, IMAGE_Y));

proxyPanel.setPreferredSize(new Dimension(IMAGE_X, IMAGE_Y));

Container c = getContentPane();

c.setLayout(new GridLayout(1, 2));

c.add(normPanel);

c.add(proxyPanel);

// Set the bounds of the frame, and the frame's default

// close operation.

setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

}

static public void main(String args[]) {

VirtualProxyTest app = new VirtualProxyTest();

app.show();

app.pack();

}

}

class NormPanel extends JPanel {

private String imageName = null;

private Image img = null;

public NormPanel(String imageName) {

this.imageName = imageName;

img = Toolkit.getDefaultToolkit().getImage(imageName);

}

public void paint(Graphics g) {

g.drawImage(img, 0, 0, getWidth(), getHeight(), this); //draw image

}

}

// ImageProxy is a proxy (or surrogate) for an image.

// The proxy delays loading the image until the first time the

// image is drawn. While the icon is loading its image, the

// proxy draws a border and the message "Loading image..."

class ImageProxy extends JPanel implements Runnable {

boolean isIconCreated = false;

private String imageName = null;

private Image img = null;

Thread imageLoadThread = null;

public ImageProxy(String imageName) {

this.imageName = imageName;

imageLoadThread = new Thread(this);

imageLoadThread.start();

}

// The proxy's paint() method is overloaded to draw a border

// and a message ("Loading image...") while the image

// loads. After the image has loaded, it is drawn. Notice

// that the proxy does not load the image until it is

// actually needed.

public void paint(Graphics g) {

if(isIconCreated) {

g.drawImage(img, 0, 0, getWidth(), getHeight(), this); //draw image

}

else {

g.drawRect(1, 1, getWidth() - 2, getHeight() - 2);

g.drawString("Loading image...", 20, 20);

}

}

public void run() {

try {

// Slow down the image-loading process.

Thread.currentThread().sleep(2000);

// create the image

img = Toolkit.getDefaultToolkit().getImage(imageName);

isIconCreated = true;

} catch(InterruptedException ex) {

ex.printStackTrace();

}

repaint();

}

}

以下示例取自Thinking in C++ 2nd,演示了Copy-on-Write技术的实现方法,耐心的Bruce Eckel用软件工程领域经典的Dog&DogHouse的例子清晰地告诉了我们应该如何实现Copy-on-Write(除了两处注释外,所有代码出自Bruce Eckel)。此外,关于string类中如何运用Copy-on-Write的问题,见参考1:

//: C12:ReferenceCounting.cpp

// From Thinking in C++, 2nd Edition

// Available at http://www.BruceEckel.com

// (c) Bruce Eckel 2000

// Copyright notice in Copyright.txt

// Reference count, copy-on-write

#include <string>

#include <iostream>

using namespace std;

// assistant function

inline void require(bool requirement,

const std::string& msg = "Requirement failed"){

using namespace std;

if (!requirement) {

fputs(msg.c_str(), stderr);

fputs("\n", stderr);

exit(1);

}

}

// Subject class

class Dog {

string nm;

int refcount;

Dog(const string& name)

: nm(name), refcount(1) {

cout << "Creating Dog: " << *this << endl;

}

// Prevent assignment:

Dog& operator=(const Dog& rv);

public:

// Dogs can only be created on the heap:

static Dog* make(const string& name) {

return new Dog(name);

}

Dog(const Dog& d)

: nm(d.nm + " copy"), refcount(1) {

cout << "Dog copy-constructor: "

<< *this << endl;

}

~Dog() {

cout << "Deleting Dog: " << *this << endl;

}

void attach() {

++refcount;

cout << "Attached Dog: " << *this << endl;

}

void detach() {

require(refcount != 0);

cout << "Detaching Dog: " << *this << endl;

// Destroy object if no one is using it:

if(--refcount == 0) delete this;

}

// Conditionally copy this Dog.

// Call before modifying the Dog, assign

// resulting pointer to your Dog*.

Dog* unalias() {

cout << "Unaliasing Dog: " << *this << endl;

// Don't duplicate if not aliased:

if(refcount == 1) return this;

--refcount;

// Use copy-constructor to duplicate:

return new Dog(*this);

}

void rename(const string& newName) {

nm = newName;

cout << "Dog renamed to: " << *this << endl;

}

friend ostream&

operator<<(ostream& os, const Dog& d) {

return os << "[" << d.nm << "], rc = "

<< d.refcount;

}

};

// Proxy class

class DogHouse {

Dog* p;

string houseName;

public:

DogHouse(Dog* dog, const string& house)

: p(dog), houseName(house) {

cout << "Created DogHouse: "<< *this << endl;

}

DogHouse(const DogHouse& dh)

: p(dh.p),

houseName("copy-constructed " +

dh.houseName) {

p->attach();

cout << "DogHouse copy-constructor: "

<< *this << endl;

}

DogHouse& operator=(const DogHouse& dh) {

// Check for self-assignment:

if(&dh != this) {

houseName = dh.houseName + " assigned";

// Clean up what you're using first:

p->detach();

p = dh.p; // Like copy-constructor

p->attach();

}

cout << "DogHouse operator= : "

<< *this << endl;

return *this;

}

// Decrement refcount, conditionally destroy

~DogHouse() {

cout << "DogHouse destructor: "

<< *this << endl;

p->detach();

}

void renameHouse(const string& newName) {

houseName = newName;

}

void unalias() { p = p->unalias(); }

// Copy-on-write. Anytime you modify the

// contents of the pointer you must

// first unalias it:

void renameDog(const string& newName) {

unalias();

p->rename(newName);

}

// ... or when you allow someone else access:

Dog* getDog() {

unalias();

return p;

}

friend ostream&

operator<<(ostream& os, const DogHouse& dh) {

return os << "[" << dh.houseName

<< "] contains " << *dh.p;

}

};

int main() {

DogHouse

fidos(Dog::make("Fido"), "FidoHouse"),

spots(Dog::make("Spot"), "SpotHouse");

cout << "Entering copy-construction" << endl;

DogHouse bobs(fidos);

cout << "After copy-constructing bobs" << endl;

cout << "fidos:" << fidos << endl;

cout << "spots:" << spots << endl;

cout << "bobs:" << bobs << endl;

cout << "Entering spots = fidos" << endl;

spots = fidos;

cout << "After spots = fidos" << endl;

cout << "spots:" << spots << endl;

cout << "Entering self-assignment" << endl;

bobs = bobs;

cout << "After self-assignment" << endl;

cout << "bobs:" << bobs << endl;

// Comment out the following lines:

cout << "Entering rename(\"Bob\")" << endl;

bobs.getDog()->rename("Bob");

cout << "After rename(\"Bob\")" << endl;

return 0;

} ///:~

参考:

1、More Effective C++ Item M29:引用计数

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有