• 代理模式


    代理模式

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

    代理模式是对象的结构模式,代理模式给某一个对象提供一个代理对象,并由此代理对象控制对原代理对象的引用。代理模式不应该让用户感觉到代理的存在,所以代理对象和原对象的对外的调用接口是一致的。

    代理模式一般包括三个角色:

    • 抽象主题角色(Subject):它的作用是统一接口。此角色定义了真实主题角色和代理主题角色共用的接口,这样就可以在使用真实主题角色的地方使用代理主题角色。
    • 真实主题角色(RealSubject):隐藏在代理角色后面的真实对象。
    • 代理主题角色(ProxySubject):它的作用是代理真实主题,在其内部保留了对真实主题角色的引用。它与真实主题角色都继承自抽象主题角色,保持接口的统一。它可以控制对真实主题的存取,并可能负责创建和删除真实对象。代理角色并不是简单的转发,通常在将调用传递给真实对象之前或之后执行某些操作,当然你也可以只是简单的转发。 与适配器模式相比:适配器模式是为了改变对象的接口,而代理模式并不能改变所代理对象的接口。

    思维导图:

    使用场景:实现延迟加载。

    <?php/**
     * Proxy design pattern (lazy loading)@global
     * 实现了图片的延迟加载
     */
    
    /* 抽象主题角色,提供统一接口 */
    interface ImageInterface
    {
        public function display();
    }
    
    /* 真是主题角色,就是需要被代理的类 */
    class Image implements ImageInterface
    {
        protected $filename;
        public function  __construct($filename) {
            $this->filename = $filename;
            $this->loadFromDisk();
        }
        protected function loadFromDisk() {
            echo "Loading {$this->filename}
    ";
        }
        public function display() {
            echo "Display {$this->filename}
    ";
        }
    }
    
    /* 代理主题角色,原图片类在实例化时就把图片加载到内存了,用代理修改为display时才执行加载 */
    class ProxyImage implements ImageInterface
    {
        protected $id;
        protected $image;
        protected $filename;
    
        public function  __construct($filename) {
            $this->filename = $filename;
        }
        public function display() {
            if (null === $this->image) { //缓存
                $this->image = new Image($this->filename);
    
            }
            return $this->image->display();
        }
    }
    
    //调用Image类,在实例化时就加载到内存了,如果没调display就浪费了
    $filename = 'test.png';
    $image1 = new Image($filename); // 已经加载到内存
    echo $image1->display();
    
    //代理类是在display时才把图片从磁盘加入内存
    $image2 = new ProxyImage($filename);
    echo $image2->display(); // 此时才加载到内存
    echo $image2->display(); // 直接从内存获取

    通过反射来代理多个对象,这种方式有缺陷,看代码体会!具体使用还看应用场景

    <?PHP
    /**
     * 使用反射实现代理工厂
     */
    
    /**
     * 真实主题角色 A
     */
    final class RealSubjectA {
    
        public function __construct() {
        }
    
        public function actionA() {
            echo "actionA method in RealSubject A <br />
    ";
        }
    
    }
    
    /**
     * 真实主题角色 B
     */
    final class RealSubjectB {
    
        public function __construct() {
        }
    
        public function actionB() {
            echo "actionB method in RealSubject B <br />
    ";
        }
    
    }
    
    /**
     * 代理主题角色
     */
    final class ProxySubject {
    
        private $_real_subjects = NULL;
    
        public function __construct() {
            $this->_real_subjects = array();
        }
    
        /**
         * 动态添加真实主题
         * @param type $subject
         */
        public function addSubject($subject) {
            $this->_real_subjects[] = $subject;
        }
    
        public function __call($name, $args) {
            foreach ($this->_real_subjects as $real_subject) {
    
                /* 使用反射获取类及方法相关信息  */
                $reflection = new ReflectionClass($real_subject);
    
                /* 如果不存在此方法,下一元素 */
                if (!$reflection->hasMethod($name)) {
                    continue;
                }
    
                $method = $reflection->getMethod($name);
    
                /* 判断方法是否为公用方法并且是否不为抽象方法 */
                if ($method && $method->isPublic() && !$method->isAbstract()) {
                    $this->_beforeAction();
    
                    $method->invoke($real_subject, $args);
    
                    $this->_afterAction();
    
                    break;
                }
            }
        }
    
        /**
         * 请求前的操作
         */
        private function _beforeAction() {
            echo "Before action in ProxySubject<br />
    ";
        }
    
        /**
         * 请求后的操作
         */
        private function _afterAction() {
            echo "After action in ProxySubject<br />
    ";
        }
    
    }
    
    
    $subject = new ProxySubject();
    
    $subject->addSubject(new RealSubjectA());
    $subject->addSubject(new RealSubjectB());
    
    $subject->actionA();
    $subject->actionB();
  • 相关阅读:
    Scilab 的画图函数(2)
    Webapp的display-name问题
    记录:在老XPS1330上安装CentOS7
    包含Blob字段的表无法Export/Import
    记一段脚本的诞生
    一个短小的JS函数,用来得到仅仅包含不重复元素的数组
    然并卵
    Linux下的定时任务Crontab
    两段用来启动/重启Linux下Tomcat的Perl脚本
    JavaScript中给二维数组动态添加元素的质朴方法
  • 原文地址:https://www.cnblogs.com/leezhxing/p/4170084.html
Copyright © 2020-2023  润新知