Foros del Web » Programando para Internet » PHP » Frameworks y PHP orientado a objetos »

Patron decorador

Estas en el tema de Patron decorador en el foro de Frameworks y PHP orientado a objetos en Foros del Web. Me pregunto si el patron decorator esta o no bien aplicado...... Código PHP: <?php class  html  {   protected  $html ;         function  __construct ( $html ...
  #1 (permalink)  
Antiguo 17/07/2011, 14:00
Avatar de Italico76  
Fecha de Ingreso: abril-2007
Mensajes: 3.303
Antigüedad: 13 años, 3 meses
Puntos: 292
Patron decorador

Me pregunto si el patron decorator esta o no bien aplicado......

Código PHP:
<?php
class html {
  protected 
$html;  
   
  function 
__construct($html){
    
$this->setHtml($html);
  }  
   
  function 
__tostring(){
    return 
$this->html;
  }
  
  function 
setHtml($html){
    
$this->html =$html;
  }
#

/* decorator */
abstract class tag extends html 
  protected 
$obj;
  
  function 
__construct ($obj=null){
    
$this->obj $obj;
  }   

#  
  
class link extends tag {
  private 
$uri;

  function 
__construct ($x=null){
    if (
$x instanceof tag){
      
parent::__construct($x);
    }else{
      
$y = new html($x);
      
parent::__construct($x);
      return 
$y;
    }
  }
  
  function 
setUri($uri){
    
$this->uri $uri;
  }

  function 
linkear (){
    
$this->setHtml ("<a href='{$this->uri}'>{$this->obj}</a>");
  }
  

  
 
class strong extends tag {

  function 
render(){
    
$this->setHtml ('<strong>'.$this->obj.'</strong>');
  }  
  
#

class italic extends tag {

  function 
render(){
    
$this->setHtml ('<i>'.$this->obj.'</i>');
  }  
  
#

class img extends html {
  private 
$img;
  
  function 
__construct($img){
    
$this->img $img;
    
$this->render();
  }  
  
  private function 
render(){
    
$this->setHtml ("<img src='{$this->img}' />");
  }
#

/* Creo HTML */
//$x = new html('Google');

/* Creo link sobre el HTML on_the_fly */
$h = new link('Google');
$h->setUri('http://google.es');
$h->linkear();

/* Le hago un strong */
$h = new strong ($h);
$h->render();

/* Le hago italica   */
$h = new italic ($h);
$h->render();

echo 
$h;

/* Creo imagen y le aplico enlace */
$i = new img ('http://2.bp.blogspot.com/-nURw-T8ErDM/ThYBg4eyhGI/AAAAAAAAB8I/Tapme43aapE/s72-c/google-picasa-blogger.jpg');
$i = new link($i);
$i->setUri('http://google.es');
$i->linkear();

echo 
$i;
Salida de la primera parte:
Cita:
<i><strong><a href='http://www.google.com'>Google</a></strong></i>
GRACIAS
__________________
Salu2!

Última edición por Italico76; 17/07/2011 a las 19:37
  #2 (permalink)  
Antiguo 18/07/2011, 10:01
 
Fecha de Ingreso: diciembre-2009
Ubicación: Misiones
Mensajes: 867
Antigüedad: 10 años, 7 meses
Puntos: 65
Respuesta: Patron decorador

no tenés bien definida una interfaz
tus decoradores están aceptando objetos de cualquier tipo

Debe ser algo como:
tu clase Html va a definir la interfaz de los componentes a los que se pueden añadir funcionalidades

tu clase Tag (interfaz para los decoradores concretos) extiende a Html (Componentes) y tus decoradores concretos (Strong, Italic, etc) extienden a Tag por lo que son también componentes

Si te fijás en el diagrama uml del patrón (o sea la base), hay una realación de agregación, un decorador agrega componentes
en el constructor de tu clase Link no debes preguntar por el tipo de datos, ya que podés especificar qué tipo de datos debe aceptar el que es pasado por parámetro

Código PHP:
Ver original
  1. public function  __construct(Html $decorador) {
  2.         $this->_html = "<a href=''>". $decorador ."</a>";
  3.     }

en vez de
Código PHP:
Ver original
  1. if ($x instanceof tag){

fijate el diagrama uml http://es.wikipedia.org/wiki/Decorat...dise%C3%B1o%29
acá explica los participantes http://dmi.uib.es/people/yuhua/TAP04-05/3.6.pdf

¿otra es que Tu clase Img que funcionalidad daría? creo que ese objeto debe recibir funcionalidad, como ser la de un enlace.

Aclaro que recién estoy tratando de comprender los patrones, hay cosas que por ahi no tengo en cuenta pero a partir de esto muchos pueden dar aportes/correcciones que nos ayuden a aprender jeje

Asi que aprovecho para tirar mi ejemplo y que salgan correcciones
Código PHP:
Ver original
  1. <?php
  2. abstract class Html
  3. {
  4.     protected $_html;
  5.    
  6.     public function render(){
  7.         return $this->_html;
  8.     }
  9.    
  10. }
  11.  
  12. class Texto extends Html
  13. {
  14.     public function  __construct($texto)
  15.     {
  16.         $this->_html = $texto;
  17.     }
  18. }
  19.  
  20. abstract class Decoradores extends Html
  21. {
  22.     public function  __construct(Html $decorador)
  23.     {
  24.         $this->_html = $decorador->render();
  25.     }
  26. }
  27.  
  28. class Link extends Decoradores
  29. {
  30.     public function render()
  31.     {
  32.         return $this->_html = "<a href=''>". $this->_html ."</a>";
  33.     }
  34. }
  35.  
  36. Class Italica extends Decoradores
  37. {
  38.     public function  render()
  39.     {
  40.         return $this->_html = "<i>". $this->_html ."</i>";
  41.     }
  42. }
  43.  
  44. $t = new Texto("Mi texto");
  45. echo $t->render() . "<br /><br />";
  46.  
  47. $i = new Italica(new Texto("Texto itálica"));
  48. echo $i->render() . "<br /><br />";
  49.  
  50. $link = new Link(new Italica(new Texto("Un link itálica")));
  51. echo $link->render();

La clase texto es un componente al que se pueden dar funcionalidades pero el no puede añadir funcionalidad a otro, no es un decorador.

este es el uml y está la clase Img que lo colocaría como componente

Última edición por Dany_s; 18/07/2011 a las 10:32
  #3 (permalink)  
Antiguo 18/07/2011, 10:35
Avatar de Italico76  
Fecha de Ingreso: abril-2007
Mensajes: 3.303
Antigüedad: 13 años, 3 meses
Puntos: 292
Respuesta: Patron decorador

Cita:
Si te fijás en el diagrama uml del patrón (o sea la base), hay una realación de agregación, un decorador agrega componentes
en el constructor de tu clase Link no debes preguntar por el tipo de datos, ya que podés especificar qué tipo de datos debe aceptar el que es pasado por parámetro
Mejor asi ? sigo preguntando..pero porque quiero que LINK cree enlaces sobre strings sin necesidad de crear previamente un objeto compatible

Código PHP:
<?php
class html {
  protected 
$html;  
   
  function 
__construct($html){
    
$this->setHtml($html);
  }  
   
  function 
__tostring(){
    return 
$this->html;
  }
  
  function 
setHtml($html){
    
$this->html =$html;
  }
#

/* decorator */
abstract class tag extends html 
  protected 
$obj;
  
  function 
__construct (tag $obj=null){
    
$this->obj $obj;
  }   

#  
  
class link extends tag {
  private 
$uri;

  function 
__construct ($obj=null){
    if (!(
$obj instanceof tag)){
      if (!
is_object($obj)){
        
$obj = (string) $obj;
      }
    }  
    
$this->obj $obj;
      
  }  
  
  function 
setUri($uri){
    
$this->uri $uri;
  }

  function 
linkear (){
    
$this->setHtml ("<a href='{$this->uri}'>{$this->obj}</a>");
  }
  

  
 
class strong extends tag {

  function 
render(){
    
$this->setHtml ('<strong>'.$this->obj.'</strong>');
  }  
  
#

class italic extends tag {

  function 
render(){
    
$this->setHtml ('<i>'.$this->obj.'</i>');
  }  
  
#

class img extends html {
  private 
$img;
  
  function 
__construct($img){
    
$this->img $img;
    
$this->render();
  }  
  
  private function 
render(){
    
$this->setHtml ("<img src='{$this->img}' />");
  }
#


/* Creo imagen y le aplico enlace */
$i = new img ('http://2.bp.blogspot.com/-nURw-T8ErDM/ThYBg4eyhGI/AAAAAAAAB8I/Tapme43aapE/s72-c/google-picasa-blogger.jpg');
$i = new link($i);
$i->setUri('http://google.es');
$i->linkear();

/* Creo link sobre el HTML on_the_fly */
$h = new link('Google');
$h->setUri('http://google.es');
$h->linkear();

/* Le hago un strong */
$h = new strong ($h);
$h->render();

/* Le hago italica   */
$h = new italic ($h);
$h->render();

echo 
$i;
echo new 
html('<p/>');
echo 
$h;
__________________
Salu2!

Última edición por Italico76; 18/07/2011 a las 10:50
  #4 (permalink)  
Antiguo 18/07/2011, 11:03
Avatar de Italico76  
Fecha de Ingreso: abril-2007
Mensajes: 3.303
Antigüedad: 13 años, 3 meses
Puntos: 292
Respuesta: Patron decorador

Te falto algo...

Cita:
class Link extends Decoradores
{
* * public function render()
* * {
* * * * return $this->_html = "<a href=''>". $this->_html ."</a>";
* * }
}
Fijate que haces: <a href='ACA NADA'>". $this->_html ."</a>
__________________
Salu2!
  #5 (permalink)  
Antiguo 18/07/2011, 11:17
Avatar de GatorV
$this->role('moderador');
 
Fecha de Ingreso: mayo-2006
Ubicación: /home/ams/
Mensajes: 38.567
Antigüedad: 14 años, 1 mes
Puntos: 2135
Respuesta: Patron decorador

Recuerda el punto clave, los Decoradores son para agregar/decorar un objeto base y/o modificar su resultado de la clase base. Esto es que los decoradores no deben de funcionar por si solos, y solo es un agregado a la clase, revisa este ejemplo que diseñe:

Código PHP:
Ver original
  1. $html = new Html();
  2. $html->addTag(new Link(new Img('foto.jpg'), 'http://www.php.net/'));
  3. $html->addTag(new Bold(new Node('Test')));
  4. echo htmlentities($html);

En este caso hay dos nodos base, Img, y Node, que son nodos base que no pueden decorar a otro nodo (no puedes hacer <img><algo></img>).

Las clases que use son las siguientes:
Código PHP:
Ver original
  1. // Base Tag Class
  2. abstract class Tag
  3. {
  4.     abstract public function render();
  5.    
  6.     public function __toString()
  7.     {
  8.         return $this->render();
  9.     }
  10. }
  11.  
  12. // Base Decorator Class
  13. abstract class TagDecorator extends Tag
  14. {
  15.     protected $_tag;
  16.    
  17.     public function __construct(Tag $tag)
  18.     {
  19.         $this->_tag = $tag;
  20.     }
  21.    
  22.     public function getTag()
  23.     {
  24.         return $this->_tag;
  25.     }
  26. }
  27.  
  28. class Html
  29. {
  30.     private $_tags = array();
  31.     public function __construct(){}
  32.    
  33.     public function addTag(Tag $tag)
  34.     {
  35.         $this->_tags[] = $tag;
  36.     }
  37.    
  38.     public function renderHtml()
  39.     {
  40.         $strOutput = '';
  41.         foreach ($this->_tags as $Tag) {
  42.             $strOutput = (string) $Tag;
  43.         }
  44.        
  45.         return $strOutput;
  46.     }
  47.    
  48.     public function __toString()
  49.     {
  50.         return $this->renderHtml();
  51.     }
  52. }
  53.  
  54. /** Base Clases **/
  55. class Node extends Tag
  56. {
  57.     private $_text;
  58.    
  59.     public function __construct($text)
  60.     {
  61.         $this->_text = $text;
  62.     }
  63.    
  64.     public function render()
  65.     {
  66.         return $this->_text;
  67.     }
  68. }
  69.  
  70. class Img extends Tag
  71. {
  72.     private $_path;
  73.     private $_border;
  74.    
  75.     public function __construct($path, $border = 0)
  76.     {
  77.         $this->_path = $path;
  78.         $this->_border = $border;
  79.     }
  80.    
  81.     public function render()
  82.     {
  83.         return '<img src="' . $this->_path . '" border="'.$this->_border.'" />');
  84.     }
  85. }
  86.  
  87. /** Decorators **/
  88.  
  89. class Link extends TagDecorator
  90. {  
  91.     private $_url;
  92.    
  93.     public function __construct(Tag $text, $url)
  94.     {
  95.         parent::__construct($text);
  96.         $this->setUrl($url);
  97.     }
  98.    
  99.     public function setUrl($url)
  100.     {
  101.         $this->_url = $url;
  102.     }
  103.    
  104.     public function render()
  105.     {
  106.         return '<a href="' . $this->_url . '">' . $this->getTag() . '</a>';
  107.     }
  108. }
  109.  
  110. class Bold extends TagDecorator
  111. {  
  112.     public function render()
  113.     {
  114.         return '<b>' . $this->getTag() . '</b>';
  115.     }
  116. }
  117.  
  118. class Italic extends TagDecorator
  119. {
  120.     public function render()
  121.     {
  122.         return '<i>' . $this->getTag() . '</i>';
  123.     }
  124. }

Saludos.

Última edición por GatorV; 18/07/2011 a las 11:26
  #6 (permalink)  
Antiguo 18/07/2011, 12:33
Avatar de Italico76  
Fecha de Ingreso: abril-2007
Mensajes: 3.303
Antigüedad: 13 años, 3 meses
Puntos: 292
Respuesta: Patron decorador

GatorV: mil gracias por tomarte el trabajo de hacer el ejemplo explicativo

Lo bueno de como aplicas el patron es que todas las clases son tipo 'Tag' o compatibles pero no entiendo esta aclaracion tuya en el contexto de mi codigo:

Cita:
Recuerda el punto clave, los Decoradores son para agregar/decorar un objeto base y/o modificar su resultado de la clase base. Esto es que los decoradores no deben de funcionar por si solos,
No lo comprendo a pesar de que me doy cuenta intuitivamente de que estas en lo cierto

Gracias de nuevo GatorV!
__________________
Salu2!
  #7 (permalink)  
Antiguo 18/07/2011, 13:40
Avatar de GatorV
$this->role('moderador');
 
Fecha de Ingreso: mayo-2006
Ubicación: /home/ams/
Mensajes: 38.567
Antigüedad: 14 años, 1 mes
Puntos: 2135
Respuesta: Patron decorador

Eso mismo, que un decorador por sí solo no funciona, necesita la clase a decorar, de nada serviría que agregaras un tag Bold si no tienes ningún texto.

La idea es modificar la funcionalidad/agregar funcionalidad a la clase base sin tener que modificar directamente la clase base, y que esta funcionalidad se pueda extender a otras clases bases.

Saludos.
  #8 (permalink)  
Antiguo 19/07/2011, 09:14
 
Fecha de Ingreso: diciembre-2009
Ubicación: Misiones
Mensajes: 867
Antigüedad: 10 años, 7 meses
Puntos: 65
Respuesta: Patron decorador

Tenes que solucionar lo de preguntar el tipo.
Código PHP:
Ver original
  1. function __construct ($obj=null){
  2.     if (!($obj instanceof tag)){
  3.       if (!is_object($obj)){
  4.         $obj = (string) $obj;
  5.       }
  6.     }  
  7.     $this->obj = $obj;
  8.   }

con eso estás desaprovechando el polimorfismo y la herencia (o interfaces si era el caso), todos tus ojetos que pueden ser decorados y los que decoran son del mismo tipo, por lo tanto van a responder a las mismas operaciones

y el constructor está aceptando objetos de cualquier tipo

Fijate en la Clase Html del ejemplo de gator_v, el método addTag(Tag $tag) acepta objetos de tipo Tag, depende de Tag y no importa si le pasas un Link, Bold, o Img porque ellos son de tipo Tag

Última edición por Dany_s; 19/07/2011 a las 09:24

Etiquetas: patron, php
Atención: Estás leyendo un tema que no tiene actividad desde hace más de 6 MESES, te recomendamos abrir un Nuevo tema en lugar de responder al actual.
Respuesta

SíEste tema le ha gustado a 2 personas




La zona horaria es GMT -6. Ahora son las 10:57.