2009-06-04 67 views
28

假設有一個名爲「Class_A」的類,它具有一個名爲「func」的成員函數。如何在PHP中實現裝飾器?

我希望「func」通過在裝飾類中包裝Class_A來做一些額外的工作。

$worker = new Decorator(new Original()); 

有人可以舉個例子嗎?我從來沒有使用OO與PHP。

以下版本是否正確?

class Decorator 
{ 
    protected $jobs2do; 

    public function __construct($string) { 
     $this->jobs2do[] = $this->do; 
    } 

    public function do() { 
     // ... 
    } 
} 

上面的代碼打算把一些額外的工作放到數組中。

+1

你應該初始化您的變量:保護$ jobs2do =陣列(); – soulmerge 2009-06-04 10:18:41

+0

但這仍然不是標準版本,也許應該使用像'call_user_func' – omg 2009-06-04 10:33:36

+0

哦,沒有看到!當然,PHP 5.3之前沒有關閉。你一定要看看PHP中的回調函數: http://us.php.net/call-user-func – soulmerge 2009-06-04 10:38:56

回答

34

這是很容易的,特別是在像PHP動態類型語言:

class Text { 

    protected $string; 

    /** 
    * @param string $string 
    */ 
    public function __construct($string) { 
     $this->string = $string; 
    } 

    public function __toString() { 
     return $this->string; 
    } 
} 

class LeetText { 

    protected $text; 

    /** 
    * @param Text $text A Text object. 
    */ 
    public function __construct($text) { 
     $this->text = $text; 
    } 

    public function __toString() { 
     return strtr($this->text->__toString(), 'eilto', '31170'); 
    } 
} 

$text = new LeetText(new Text('Hello world')); 
echo $text; // H3110 w0r1d 

你可能想看看在wikipedia article了。

+0

謝謝,但是如何將一個新的成員函數堆到一個數組?看起來我的當前版本會引起警告。 – omg 2009-06-04 09:57:20

+0

爲什麼$ string和$ text不是私有的? – 2009-06-04 10:20:44

+0

你當然可以把它變成私人的:) – omg 2009-06-04 10:32:59

38

我建議你也爲裝飾者和你想裝飾的物體創建一個統一的界面(或者甚至抽象基類)。

要繼續提供,你可以有像上面的例子:

interface IDecoratedText 
{ 
    public function __toString(); 
} 

那當然修改TextLeetText實現接口。

class Text implements IDecoratedText 
{ 
...//same implementation as above 
} 

class LeetText implements IDecoratedText 
{  
    protected $text; 

    public function __construct(IDecoratedText $text) { 
     $this->text = $text; 
    } 

    public function __toString() { 
     return str_replace(array('e', 'i', 'l', 't', 'o'), array(3, 1, 1, 7, 0), $this->text->toString()); 
    } 

} 

爲什麼使用接口?

因爲那麼您可以根據需要添加儘可能多的裝飾器,並確保每個裝飾器(或要裝飾的對象)都具有所有必需的功能。

+1

這就是爲什麼我說這在PHP中特別容易:你不需要這樣的東西:) 如果你真的想要類型安全,你應該使用另一種語言。 – soulmerge 2009-06-04 05:01:33

+0

我沒有看到在這裏使用接口的好處 – 2009-06-04 08:45:07

+0

謝謝,但如何將一個新的成員函數堆棧到一個數組?似乎我的當前版本將導致警告。 – omg 2009-06-04 09:57:05

5

我想用裝飾來鼓勵同事使用緩存更多,並且靈感來自用PHP反射實驗的漂亮的Python語法來僞造這種語言功能(並且我必須強調'假')。這是一個有用的方法。這裏有一個例子:

class MrClass { 
    /** 
    * decoratorname-paramname: 50 
    * decoratorname-paramname2: 30 
    */ 
    public function a_method($args) { 
    // do some stuff 
    } 
} 


class DecoratorClass { 
    public function __construct($obj) { 
    $this->obj = $obj; 
    $this->refl = new ReflectionClass($obj); 
    } 
    public function __call($name, $args) { 
    $method = $this->refl->getMethod($name); 
    // get method's doccomment 
    $com = trim($method->getDocComment()); 
    // extract decorator params from $com 
    $ret = call_user_func_array(array($this->obj, $name), $args); 
    // perhaps modify $ret based on things found in $com 
    return $ret; 
} 

更好的例子在這裏緩存例子:https://github.com/mrmonkington/EggCup/

+0

這是更多類型的代理模式! – 2014-02-05 17:46:04

20

以上答案都不正確,優雅實現Decorator。 mrmonkington的答案非常接近,但您不需要使用反射來在PHP中制定Decorator模式。在另一個線程中,@Gordon shows how to use a decorator for logging SOAP activity。下面是他是如何做的:

class SoapClientLogger 
{ 
    protected $soapClient; 

    // this is standard. Use your constuctor to set up a reference to the decorated object. 
    public function __construct(SoapClient $client) 
    { 
     $this->soapClient = $client; 
    } 

    ... overridden and/or new methods here ... 

    // route all other method calls directly to soapClient 
    public function __call($method, $args) 
    { 
     // you could also add method_exists check here 
     return call_user_func_array(array($this->soapClient, $method), $args); 
    } 
} 

,他是一個輕微的修改,您可以通過所期望的功能的構造器:

class Decorator { 

    private $o; 

    public function __construct($object, $function_name, $function) { 
     $this->o = $object; 
     $this->$function_name = $function; 
    } 
    public function __call($method, $args) 
    { 
     if (!method_exists($this->o, $method)) { 
      throw new Exception("Undefined method $method attempt in the Url class here."); 
     } 
     return call_user_func_array(array($this->o, $method), $args); 
    } 
} 
+3

您應該改進__call()方法來支持實現fluent-api模式的對象。類似於 '$ result = call_user_func_array(array($ this-> o,$ method),$ args); return $ result === $ this-> o? $ this:$ result;' – Nat 2017-03-02 08:43:50

+0

請解釋$ function_name的用法和工作流程 – Massimo 2017-04-23 00:52:25

-2

的裝飾的一個重要和獨特的特點是一個抽象類擴展另一個。裝飾參與者既是組件參與者的包裝也是擴展組件。

<?php 
abstract class Decorator extends IComponent 
{ 
    //public function getDescription() { } 
} 
?> 

我相信這是在四人幫的目錄中發生這種情況的唯一模式。裝飾器可以很容易地向對象添加屬性而不用改變對象。對於簡單,準確和清晰的例子中看到:

http://www.php5dp.com/php-decorator-design-pattern-accessorizing-your-classes/#more-32

相關問題