2009-06-04 17 views
28

Supongamos que hay una clase llamada "Class_A", tiene una función de miembro llamada "func".¿Cómo implementar un decorador en PHP?

Quiero que el "func" haga un trabajo extra envolviendo Class_A en una clase de decorador.

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

¿Alguien puede dar un ejemplo? Nunca he usado OO con PHP.

¿Es la siguiente versión correcta?

class Decorator 
{ 
    protected $jobs2do; 

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

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

El código anterior tiene la intención de poner un poco más de trabajo en una matriz.

+1

Debe inicializar la variable: protected $ jobs2do = array(); – soulmerge

+0

Pero esa no es la versión estándar, tal vez debería usar algo como 'call_user_func' – omg

+0

¡Oh, no lo vi! Sí, por supuesto, no hay cierres antes de PHP 5.3. Definitivamente debería echar un vistazo a las devoluciones de llamadas en PHP: http://us.php.net/call-user-func – soulmerge

Respuesta

34

Eso es bastante fácil, sobre todo en un lenguaje de tipos dinámicos como 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 

Es posible que desee echar un vistazo a la wikipedia article, también.

+0

Gracias, pero ¿cómo agregar una nueva función de miembro a una matriz? Parece que mi versión actual causará una advertencia. – omg

+0

¿Por qué $ string y $ text no son privados? –

+0

Puede hacerlo privado, por supuesto :) – omg

38

Sugiero que también cree una interfaz unificada (o incluso una clase base abstracta) para los decoradores y los objetos que desee decorar.

Para continuar con el ejemplo anterior siempre que podría tener algo como:

interface IDecoratedText 
{ 
    public function __toString(); 
} 

Luego, por supuesto modificar tantoText y LeetText para implementar la interfaz.

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()); 
    } 

} 

¿Por qué utilizar una interfaz?

Porque entonces puede agregar tantos decoradores como desee y tener la seguridad de que cada decorador (u objeto a decorar) tendrá todas las funciones requeridas.

+1

Es por eso que dije que es especialmente fácil en PHP: no necesitas tales cosas :) Si realmente quieres seguridad de tipo, deberías estar usando otro idioma. – soulmerge

+0

No veo los beneficios de usar una interfaz aquí –

+0

Gracias, pero ¿cómo agregar una nueva función miembro a una matriz? Parece que mi versión actual causará una advertencia. – omg

5

Quería usar la decoración para animar a los colegas a utilizar el almacenamiento en caché más, e inspirado en la agradable sintaxis de Python experimentada con la reflexión de PHP para falsificar esta característica del lenguaje (y tengo que enfatizar 'falso'). Fue un enfoque útil. He aquí un ejemplo:

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; 
} 

Mejor ejemplos con ejemplos de almacenamiento en caché aquí: https://github.com/mrmonkington/EggCup/

+0

¡Esto es más como un patrón de Proxy! –

20

Ninguna de estas respuestas implementa Decorator correctamente y con elegancia. La respuesta de mrmonkington se acerca, pero no es necesario usar el reflejo para representar el patrón Decorator en PHP. En otro hilo, @Gordon shows how to use a decorator for logging SOAP activity. Así es como lo hace:

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); 
    } 
} 

Y es una ligera modificación en la que puede pasar la funcionalidad deseada para el constructor:

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

Debe mejorar el método __call() para admitir objetos que implementan el patrón fluent-api. algo así como '$ result = call_user_func_array (array ($ this-> o, $ method), $ args); return $ result === $ this-> o? $ this: $ result; ' – Nat

+0

explica el uso y el flujo de trabajo de $ function_name – Massimo

-2

Una característica clave y única del decorador es que una clase abstracta se extiende otro. El participante Decorador es a la vez envoltorio y amplía al participante del componente.

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

Creo que este es el único patrón en el que esto sucede en el catálogo de Gang of Four. Decorator hace que sea fácil agregar propiedades a un objeto sin cambiar el objeto. Para un ejemplo sencillo, preciso y claro ver:

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

Cuestiones relacionadas