美文网首页
设计模式——代理模式

设计模式——代理模式

作者: 东方胖 | 来源:发表于2023-08-19 23:18 被阅读0次

    Provide a surrogate or placeholder for annother obeject to control access to it.

    代理模式的结构有益于几乎所有的构建类设计模式。
    它的 UML 图类似一个三角形

    proxy uml 图,使用staruml 绘制
    class Grapic
    {
    public:
        virtual void draw() = 0;
        virtual void save() = 0;
        virtual void load() = 0;
    };
    
    
    class Image: public Grapic
    {
    public:
        void draw() {
            std::cout << "draw a image self" << std::endl; 
        }
    
        void save() {
           std::cout << "save on disk" << std::endl;
        }
    
        void load() {
             std::cout << "load from disk .." << std::endl;
        }
    
    };
    
    class ImageProxy: public Grapic
    {
    public:
        ImageProxy(const char *imageFile, Context* context) {
            _imageFile = imageFile;
            _image = nullptr;
        }
        void draw() {
              _image->draw();
        }
       void load() {
            if (_context->getUser() == UserRole::Super)
                return _image->load();
           std::cout << "have not access right" << std::endl;
          return;
      }
    
    protected:
       Image* loadImage() {
        if (_image == nullptr) {
             return new Image();
        }
        return _image;
      }
    private:
        char * imageFile;
        Image *_image;
    };
    
    

    代理模式有两个典型的特征:

    • 两个子类继承自一个父类,有时候可以是一个接口,这意味着两个子类具有一样的接口(某些virtual函数)
    • 代理类持有一个被代理的引用——这种方法在行为类模式中颇为常见。

    因为有第二条,所有代理总是可以通过这个引用操纵真实的对象。

    乍看十分别扭的结构,它会在什么场景下有用呢?
    因为 ImageProxy 几乎就是 Image的影子,但是它们只是在抽象接口上是一致的,彼此的 constructor 和 属性其实可以大不一样,Proxy可以控制对 被代理对象的访问——做到这一点,只需要在代码中,凡出现要传递 被代理对象的上层client代码中,代之以 Proxy 。

    比方说,上面的 Image 的 load 动作,它是一个抽象的接口,
    当从代理的 load 入口走进去的时候,先从上下文中获取一些类似角色的参数,校验其访问权限是否满足

    这是一种类似权限控制的访问场景。因为 new Image() 的动作完全被代理负责。

    • 第一种情况,对client 隐藏真实的对象,在client 上下文中,类似下面这种的写法
      有一个client context 的上下文长这样
    void operate(Graphic* g); 
    
    

    那么可以可以用代理调用

    operate(new ImageProxy()) ; // C++ 注意持有 代理的指针进行内存管理
    
    • 另一种情况是占位作用,一般如果类似 load() 这种的动作,是一个耗时操作,它返回一个 Image——上面的代码没有体现这点,但我们可以假设它其实是一个有 UI呈现的动作,当 load 发生时,用代理去做,而不是直接在图片中实现,这样代理可以充当一个占位符,先假设它完成了,然后上下文再在合适的时机,调用 draw 之类真实的绘制动作去完成。
      这会引来一中效果,类似网页加载,那些比较清楚的图片,总是比较慢,我们先看到大多数文字,然后一张图片的占位符,类似下面这样。然后过一段时间,这张图真实地加载出来,——我们此时可能已经读完一百多字了。


      占位图
    • 一个真实的应用是 智能指针

    智能指针 是 C++为了实现内存自动管理的一种技术——将原始的指针封装在一个有构造函数和析构函数的类中,利用栈对象自动调用析构的特性实现自动化释放内存。

    在std::auto_ptr 里可能不太能清晰地看到代理模式的三角结构。但是如果我们把运算符 -> * 看成是一个公共接口的话,auto_ptr 和 裸指针对象都支持该运算,因而可以视为一种公共的接口,这样看来,它们完全符合代理模式的三角接口。

    试用伪代码表达如下, -> 用接口 reference 表示,deReference代替 * 运算
    于是有一个“看不见”的公共接口,大概定义成这样

    template<class T>
    class PointerInterface
    {
    public:
        T* reference() = 0;
        T deReference() = 0;
    };
    
    

    Pointer 具有 reference 和deReference操作。
    于是智能指针 SmartPointer 也从PointerInterface 继承,然后持有Pointer 的句柄(引用) 这样,代理的三角结构是不是就很清晰了。
    实际的实现中。

    很关键的点也正是如此:

    • 智能指针,不管是旧的 auto_ptr 还是新版C++11的 share_ptr unique_ptr 等等都一致的重载了运算符 -> 和 符号,这里不是乘法是指针的解引用运算。
    • 其次持有裸指针的真身,从而实现对它指向的内存的“管理”

    相关文章

      网友评论

          本文标题:设计模式——代理模式

          本文链接:https://www.haomeiwen.com/subject/cbajmdtx.html