zoukankan      html  css  js  c++  java
  • 【PHP设计模式】结构型之代理(Proxy)

    代理模式(Proxy)

    <意图>

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

      代理模式给某一个对象提供一个代理对象,并由代理对象控制对源对象(被代理对象)的引用。代理就是一个人或一个机构代表另一个人或者一个机构采取行动。 (客户端引用的是代理对象)某些情况下,客户不想或者不能够直接引用一个对象,代理对象可以在客户和目标对象直接起到中介的作用。客户端分辨不出代理主题 对象与真实主题对象。代理模式可以并不知道真正的被代理对象,而仅仅持有一个被代理对象的接口,这时候代理对象不能够创建被代理对象,被代理对象必须有系 统的其他角色代为创建并传入。

    <动机>

      因为某个对象消耗太多资源,而且你的代码并不是每个逻辑路径都需要此对象, 你曾有过延迟创建对象的想法吗 ( if和else就是不同的两条逻辑路径) ? 你有想过限制访问某个对象,也就是说,提供一组方法给普通用户,特别方法给管理员用户?以上两种需求都非常类似,并且都需要解决一个更大的问题:你如何提供一致的接口给某个对象让它可以改变其内部功能,或者是从来不存在的功能?

      你怎样才能在不直接操作对象的情况下,对此对象进行访问?

    <UML>

    <总结>

      个人理解实际应用中就是相当于公有的放到一个类中,客户访问不到,但是适当的时候需要调用,特殊的私有的放在各自类中用到时调用。特殊的类就是代理类需要有公有类的引用。只需要在用到的时候才取, client只需要引用代理对象。

    两种情况:

      一、引用不到寻求代理。

      二、聚合共用部分,用到才调用,与继承相比增加对象开销但调用快,代码可复用。

    <代理模式与父类和接口的异同>

    <相同点>

      代理模式的作用和父类以及接口的作用类似,都是为了聚合共用部分,减少公共部分的代码。

    <不同点>

      相比起父类,他们的语境不同,父类要表达的含义是 is-a, 而代理要表达的含义更接近于接口, 是 has-a,而且使用代理的话应了一句话"少用继承,多用组合",要表达的意思其实也就是降低耦合度了。

      相比起接口,他们实现的功能又不太一样,语境都是has-a,不过接口是has-a-function,而代理对象时是has-a-object,这个 object是has-a-function的object,此外,接口是为了说明这个类拥有什么功能,却没有具体实现,实现了多态,而代理对象不但拥有 这个功能,还拥有这个功能的具体实现。

    <示例一>

    class Subject {
        function someMethod() {
            echo "test";
            //sleep(1); //do something
        }
    }
    
    class ProxySubject {
        private $subject;
        function ProxySubject() {
            $this->subject = new Subject();
        }
        function someMethod() {
            $this->subject->someMethod();
        }
    }
    
    $proxy = new ProxySubject();
    $proxy->someMethod();

    <结果>

    Result:test

     

    <示例二>

    class Printer {    //代理对象,一台打印机
        public function printSth() {
            echo 'I can print <br>';
        }
        // some more function below
        // ...
    }
    
    class TextShop {    //这是一个文印处理店,只文印,卖纸,不照相
        private $printer;
    
        public function __construct(Printer $printer) {
            $this->printer = $printer;
        }
    
        public function sellPaper() {    //卖纸
            echo 'give you some paper <br>';
        }
    
        public function __call($method, $args) {    //将代理对象有的功能交给代理对象处理,即文印工作
            if(method_exists($this->printer, $method)) {
                $this->printer->$method($args);
            }
        }
    }
    
     class PhotoShop {    //这是一个照相店,只文印,拍照,不卖纸
         private $printer;
    
         public function __construct(Printer $printer) {
             $this->printer = $printer;
         }
    
         public function takePhotos() {    //照相
             echo 'take photos for you <br>';
         }
    
         public function __call($method, $args) {    //将代理对象有的功能交给代理对象处理,即文印工作
             if(method_exists($this->printer, $method)) {
                 $this->printer->$method($args);
             }
        }
     }
    
    $printer = new Printer();
    $textShop = new TextShop($printer);
    $photoShop = new PhotoShop($printer);
    
    $textShop->printSth();
    $textShop->sellPaper();
    $photoShop->printSth();
    $photoShop->takePhotos();

      文印处理店和照相店都具有文印的功能,所以我们可以将文印的功能代理给一台打印机,这里打印机只有一个功能,假如打印机还有n个功能,我们使用__call()方法就能够省去很多重复的代码了假如是使用继承,这样语境上就不合理,一个店显然不应该继承一台打印机而使用接口,因为我们的功能实现都是一样,也没有必要去重新实现接口的功能,所以此处使用代理,或者我们说是组合,是最佳选择。

     上例中调试存在一个问题,就是$args是一个数组,需要将数组遍历出来如下改动

    class Printer {    //代理对象,一台打印机
        public function printSth($a) {
            echo $a.'<br> I can print <br>';
        }
    }
    class TextShop {    //这是一个文印处理店,只文印,卖纸,不照相
        private $printer;
        public function __construct(Printer $printer) {
            $this->printer = $printer;
        }
        public function sellPaper() {    //卖纸
            echo 'give you some paper <br>';
        }
        public function __call($method, $args) {    //将代理对象有的功能交给代理对象处理,即文印工作
            //return call_user_func_array(array($this->printer,$method),$args);
            if(method_exists($this->printer, $method)) {
                foreach($args as $arg){
                    $this->printer->$method($arg);
                }
            }
        }
    }
     class PhotoShop {    //这是一个照相店,只文印,拍照,不卖纸
         private $printer;
    
         public function __construct(Printer $printer) {
             $this->printer = $printer;
         }
    
         public function takePhotos() {    //照相
             echo 'take photos for you <br>';
         }
    
         public function __call($method, $args) {    //将代理对象有的功能交给代理对象处理,即文印工作
             //return call_user_func_array(array($this->printer,$method),$args);
             if(method_exists($this->printer, $method)) {
                 foreach($args as $arg){//添加遍历
                     $this->printer->$method($arg);
                 }
             }
        }
     }
    $printer = new Printer();
    $textShop = new TextShop($printer);
    $photoShop = new PhotoShop($printer);
    
    $textShop->printSth(1);
    $textShop->sellPaper();
    $photoShop->printSth(2);
    $photoShop->takePhotos();

    <结果>

    1
    I can print 
    give you some paper 
    2
    I can print 
    take photos for you 
  • 相关阅读:
    Linux后台进程管理的一些命令小结
    Linux 技巧:让进程在后台可靠运行的几种方法
    python对图片进行分割
    pytorch实现优化optimize
    pytorch实现批训练
    pytorch实现网络的保存和提取
    pytorch实现分类
    pytorch实现回归任务
    递推之数字三角形
    深搜——蓝桥杯之迷宫
  • 原文地址:https://www.cnblogs.com/colorstory/p/2720166.html
Copyright © 2011-2022 走看看