Dudas del patrón decorador y su uso con ioc containers

B

Hoy me he puesto a buscar patrones de diseño para estar un poco a la orden del día en cuanto a este tema y me he encontrado con el Decorator Pattern. He buscado muchos ejemplos y aunque parece que lo he entendido tengo dudas sobre su aplicación.

¿Este patrón vale para contenidos dinámicos en una web? Por ejemplo, pienso en el menú de usuario de Mediavida donde aparece nuestro nombre, avisos, favs, mensajes y botón salir. Si estamos logueados en la web nos aparece esa información y si estamos desconectados desaparece todo eso a favor de un enlace a iniciar sesión. Podemos decir que existen dos comportamiento diferentes en función del valor de las variables de sesión. Entonces ¿esas dos posibilidades son decoradores diferentes? ¿Una clase decorador para el caso de que el usuario esté logueado y otra clase decorador si está desconectado?

Mi segunda pregunta sería en relación a la primera en caso de que ésta sea afirmativa. He visto que el aspecto general de cualquier decorador es algo así como:

abstract class Decorador extends Componente
{
    private Componente _componente;
 
public Decorador(Componente componente)
{
        _componente = componente;
}

public void operacion()
{
        _componente.operacion();
}
}

Lo importante ahí es que toda clase que extienda de esa tiene que recibir en su constructor el componente a decorar. Pero, ¿qué sucede (insisto en si la primera pregunta que he formulado es afirmativa) si esos decoradores tienen a parte otras clases en su constructor a parte del componente a decorar? Todos los ejemplos que he encontrado en internet de decoradores tienen un solo elemento en el constructor (el componente a decorar) pero no he encontrado ninguno que tenga varios.

Volviendo al ejemplo anterior, si el usuario está logueado tengo que mostrar el nombre de usuario, el número de mensajes y el número de alertas. Mi clase decoradora tendría entonces declarada en el constructor el componente a decorar junto a un "UsersService" para obtener los datos del usuario (nombre, avatar etc...), "AlertsService" para obtener el número de alertas pendientes y "PrivateMessagesService" para obtener el número de mensajes privados sin leer.

class UserIsLoggedDecorador extends Decorador
{
    
private $componente; private $usersService; private $alertsService; private $privateMessagesService; public UserIsLoggedDecorador(Componente $componente, UserService $userService, AlertsService, PrivateMessagesService) { $this->componente = componente; $this->usersService = $usersService; [...] } public void operacion() { // Cosas } }

Sin embargo el otro decorador, para el caso en el que no esté logueado, no necesitaré tantas dependencias en el constructor ya que sólo tengo que mostrar el botón de iniciar sesión:

class UserIsNotLoggedDecorador extends Decorador
{
    
public UserIsNotLoggedDecorador(Componente $componente) { _componente = componente; } public void operacion() { // Cosas } }

Si estoy utilizando un inyector de dependencias tipo Ninject, Dagger o PHP DI, ¿cómo implemento esto? ¿Tengo que crear un factory para cada decorador y después una abstract factory que me devuelva la factory correspondiente en función de si el usuario está logueado no?

bLero

Creo que el login es un mal ejemplo para el patrón decorator. Esos 2 comportamientos se definen con un IF de toda la vida.

Te pongo un ejemplo sencillo para ver si lo entiendes mejor:

Supongamos la clase Cuadrado:

public class Cuadrado {

int lado;

public Cuadrado(int lado) {
    this.lado = lado;
}

public void dibujar() { // pintamos el cuadrado }
}

Ahora supongamos que queremos un cuadrado con un color. La solución lógica sería aplicar herencia:

public class CuadradoColoreado extends Cuadrado {

Color color;

public CuadradoColoreado(int lado, Color color) {
    super(lado);
    this.color = color;
}

public void dibujar() { // sobreescribimos el método para pintar el cuadrado del color }
}

Pero este enfoque tiene un problema. Al utilizar la herencia, estamos obligando al programador a utilizar un cuadrado o un cuadrado con color sin permitirnos primero utilizar un cuadrado, y luego añadir un color (en tiempo de ejecución).

Si ahora añadiésemos un cuadrado con bordes redondeados, tendría que extender de cuadrado (si no queremos color) y también de cuadrado con color (si queremos que también tenga color).

El patrón decorator se diferencia de la herencia en que delega la responsabilidad en las clases decoradoras, que pueden combinarse entre sí de cualquier manera e incluso en tiempo de ejecución. Esto permite tener un montón de clases decoradoras pequeñas a las que llamar cuando sea necesario, en lugar de una clase "RectanguloColorTransparenteRedondeado" que sobreescriba los métodos de sus clases padre.

2 1 respuesta
B

Vale, creo que me ha quedado un poquito más claro. Mil veces mejor que la herencias para ir añadiendo pequeñas características. Añado con otro ejemplo que me ha gustado mucho por si alguien más pasa por aquí: http://code.tutsplus.com/tutorials/design-patterns-the-decorator-pattern--cms-22641

Foxandxss

Estoy con #2. Tu necesitas un if de toda la vida.

El patrón decorator es para añadir funcionalidad extra a un objeto sin cambiar su API.

Muy típico en Java. Por ejemplo (no recuerdo los nombres de las clases exactamente):

Tienes la clase "FileReader" a la que le pasas un fichero y puedes hacer lo típico como "readLine", "read", etc. El problema es que esa clase no permite guardar en un buffer todo lo que va leyendo, y eso limita bastante su uso.

Entonces lo que hicieron en Sun, fue dejar ese FileReader como la implementación más básica de un lector de fichero y crear decoradores que vayan añadiendo diferentes funcionalidades.

Por ejemplo, BufferedReader que se usa algo así como:

BufferedReader reader = new BufferedReader(new FileReader(new File("foo.txt")));

Básicamente un decorator recibe como parámetro la clase que decora (FileReader) y este caso pues hace lo mismo que hace el original (esencial) pero con algo extra, buffering. La idea es que se usa igual "readLine", "read", etc. Solo que ahora tiene funcionalidad extra.

1 respuesta
B

#4 Tienes razón! Me pase los 2 años del módulo de FP haciendo eso, no entendía por qué, sólo sabía que me quedaba un tochaco de línea y que funcionaba y ahora efectivamente tiene todo su sentido. El profesor bien nos lo podía haber explicado no que ahora me he tenido que enterar! Que claro, hay cada uno que yo creo que no tiene mucha idea... Muchas gracias por el aporte. :) Me ha gustado eso que dices de lo "esencial" y creo que ahora sé destinguir.

Usuarios habituales