【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 

 

 

 

 

 

posted on 2014-06-22 14:56  color_story  阅读(191)  评论(0编辑  收藏  举报

导航