结构型模式--代理

1、意图

  为其他对象提供一种代理以控制对这个对象的访问。

2、结构

3、参与者

  Proxy:

    保存一个引用使得代理可以访问实体。若RealSubject和Subject的接口相同,Proxy会引用Subject。

    提供一个与Subject的接口相同的接口,这样代理就可以用来替代实体。

    控制对实体的存取,并可能负责创建和删除它。

    其他功能依赖于代理的类型:远程代理负责对请求及其参数进行编码,并向不同地址空间中的实体发送已编码的请求;虚代理可以缓存实体的附加信息,以便延迟对它的访问;保护代理检查调用者是否具有实现一个请求所必须的访问权限。

  Subject:定义RealSubject和Proxy的共有接口,这样就在任何使用RealSubject的地方都可以使用Proxy。

  RealSubject:定义Proxy所代表的实体。

4、适用性

  在需要用比较通用和复杂的对象指针代替简单地指针的时候,使用Proxy模式。下面是可以使用代理模式常见的情况:

  远程代理:为一个对象在不同的地址空间提供局部代表。

  虚代理:根据需要创建开销很大的对象。

  保护代理:控制对原始对象的访问。保护代理用于对象应该有不同的访问权限的时候。

  智能引用:取代了简单的指针,它在访问对象时执行一些附加操作。它的典型用途包括:

        对指向实际对象的引用计数,这样当该对象没有引用时,可以自动释放它(智能指针);

        当第一次引用一个持久对象时,将它装入内存;

        在访问一个实际对象前,检查是否已经锁定了它,以确保其他对象不能改变它。

5、代码示例

// Virtual Proxy, Graphic类为图形对象定义一个接口
class Graphic 
{
public:
    virtual ~Graphic(); 
    virtual void 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重定义HandleMouse操作,使得用户可以交互的调整图像的尺寸。
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具有相同的接口:
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 void Load(istream& from); 
    virtual void Save(ostream& to);
protected:
    Image* GetImage(); 
private:
    Image* _image; 
    Point _extent; 
    char* _fileName;
〕
// 构造函数保存了存储图像的文件名的本地拷贝,并初始化extent和image:
ImageProxy::ImageProxy (const char* fileName)
{
    _fileName = strdup(fileName);
    _extent = Point::Zero;// don't know extent yet
    _image = 0;
}
Image* Imageproxy::GetImage()
{
    if (_image == 0)
    {
        _image = new Image(_fileName); 
    }
    return _image;
}

// 如果可能的话,GetExtent的实现部分返回缓存的图像尺寸;否则从文件中装载图像。Draw用来装载图像,HandelMouse则向实际图像转发这个事件。
const Point& ImageProxy::GetExtent ()
{
    if(_extent == Point::Zero)
    {
        _extent = GetImage()->GetExtent();
    }
    return _extent;
}
void ImageProxy::Draw (const Point& at)
{
    GetImage()->Draw(at);     
}
void ImageProxy::HandleMouse (Event& event)
{
    GetImage()->HandleMouse(event );
}

// Save操作将缓存的图像尺寸和文件名保存在一个流中。Load得到这个信息并初始化相应的成员函数。
void ImageProxy::Save (ostream& to)
{
    to <<_extent <<_fileName;
}
void ImageProxy::Load (istream& from)
{
    from >>_extent >>_fileName;
}
// 最后,假设我们有一个类TextDocument能够包含Graphic对象
class TextDocument
{
public:
    TextDocument ()
    void Insert (Graphic*);
    // ...
};

// 我们可以用以下方式把ImageProxy插入到文本文件中。
TextDocument* text = new TextDocument;
// ...
text->Insert (new ImageProxy("anImageFileName"));

6、总结

  代理模式通过代理类控制用户对实体的访问,这种控制可以是访问权限的控制,可以是在原访问的基础上附加一些额外的信息或处理。用户直接访问的对象是代理而不是真实实体。

  代理模式分为远程代理、虚代理、保护代理、智能引用。

  代理模式中的Porxy与RealSubject,即代理与真实实体,需提供一致的对外接口,这些接口在基类Subject中声明。

posted @ 2022-05-03 17:32  流翎  阅读(23)  评论(0编辑  收藏  举报