#Intent

Provide a surrogate(替身) or placeholder for another object to control access to it;

#定义
代理模式为另一个对象提供一个替身或占位符以控制对这个对象的访问;

#动机
控制对对象的访问的一个原因是:推迟创建和初始化该对象的全部耗费,直到我们确确实实需要用它;

#适用性
代理模式适用于需要更多功能或更复杂的对象引用而不是一个简单的指针;下面是几个常见的适用代理模式的情景:

  • 远程代理:提供了一个本地代表,它可以代表另一个地址空间的对象;
  • 虚拟代理:可以在需要的时候创建那些创建开销很大的对象;
  • 保护代理:控制对原生对象的访问;
  • 智能指针:参见

#结构图
proxy

#参与者

  • 代理Proxy
    • 保存一个对真实对象的引用.若RealSubject和Subject的接口相同,Proxy会引用Subject.
    • 提供一个与Subject的接口相同的接口,这样代理就可以用来替代视体;
    • 提供对实体的存取,并可能负责创建和删除它;
    • 其他功能依赖于代理的类型:
      • Remote Proxy负责对请求及其参数进行编码,并向不同地址空间中的实体发送已编码的请求;
      • Virtual proxy可以缓存实体的附加信息,以便延迟对它的访问;例如,动机一节中提到的ImageProxy缓存了图像实体的尺寸;
      • Protection Proxy检查调用者是否具有实现一个请求所必须的访问权限;
  • 对象接口(Subject)
    • 定义RealSubject 和Proxy的公用接口,这样就可以在任何使用RealSubject的地方都可以使用Proxy;
  • 真实对象RealSubject
    • 定义Proxy所代表的实体;

#Consequences
Proxy模式在访问对象时引入了一定程度的间接性;

  1. Remote proxy可以隐藏一个对象存在于不同地址空间的事实;
  2. virtual proxy可以进行优化,例如根据要求创建对象;
  3. Protection Proxy和Smart reference都允许在访问一个对象时有一些附加的Housekeeping task;
    #Implementation
    一个虚拟代理:定义了接口
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class Graphic {
    public:
    virtual ~Grahpic();
    virtual viod Draw(const Point& at) = 0;
    virtual void HandleMouse(Event& event) = 0;
    virtual const Point& GetExtent() = 0;
    virtual void Load(istream& from) = 0;
    virtual void Save(ostream& to) = 0;
    protected:
    Graphic();
    };

Image类实现了Graphic接口用来显示图像文件.Image override了Handlemouse操作,使得用户可以交互的调整图像的尺寸;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Image:public Graphic {
public:
Image(const char* file); // loads image from a file
virtual ~Image();
virtual void Draw(const Point& at);
virtual void HandleMouse(Event& event);
virtual const Point& GetExtent();
virtual void Load(istream& from);
virtual void Save(ostream& to);
private:
// ...
};

ImageProxy 和 Image具有相同的接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class ImageProxy : public Graphic {
public:
ImageProxy(const char* imageFile);
virtual ~ImageProxy();
virtual void Draw(const Point& at);
virtual void HandleMouse(Event& event);
virtual const Point& getExtent();
virtual viod Load(istream& from);
virtual void Save(ostream& to);
protected:
Image* GetImage();
private:
Image* _image;
Point _extent;
char* _fileName;
}

构造函数保存了文件名的本地拷贝,并且初始化了成员变量

1
2
3
4
5
ImageProxy::ImageProxy(const char* filename) {
_fileName = strdup(fileName);
_extent = Point::Zero;
_image = 0;
}

1
2
3
4
5
6
Image* ImageProxy::GetImage() {
if(_image == 0){
_image = new Image(_fileName);
}
return _image;
}

如果可能的话,GetExtent返回的是缓存过图像的extent;否则从文件中读取图像(开销大);这和单例模式很像;

1
2
3
4
5
6
const Point& ImageProxy::GetExtent() {
if(_extent == Point::Zero) {
_extent = GetImage()->GetExtent();
}
return _extent;
}

1
2
3
4
5
6
7
void ImageProxy::Draw(const Point& at) {
GetImage()->Draw(at);
}
void ImageProxy::HandleMouse(Event& event) {
GetImage()->HandleMouse(event);
}

save操作将缓存的图像尺寸和文件名保存在一个流中.Load得到这个信息并初始化相应的成员函数;

1
2
3
4
5
6
7
void ImageProxy::Save(ostream& to) {
to << _extent << _fileName;
}
void ImageProxy::Load(istream& from) {
from >> _extent >> _fileName;
}

最终,假设我们有一个文本文档类,包含了Graphic对象:

1
2
3
4
5
class TextDocument {
public:
TextDocument();
void Insert(Graphic*);
}

我们就可以使用图像代理来代替真实的图像对象来插入文本文档,以实现推迟加载图片文件的效果,加快文档的打开速度;
`c++ TextDocument* text = new TextDocument(); text->Insert(new GraphicProxy("anImageFileName"));

Comments

2015-03-28

⬆︎TOP