Patrón de diseño Decorator en Java Ejemplo

El patrón de diseño Decorator se utiliza para modificar la funcionalidad de un objeto en tiempo de ejecución. Al mismo tiempo, otras instancias de la misma clase no se verán afectadas por esto, por lo que cada objeto individual obtiene un comportamiento modificado. El patrón de diseño Decorator es uno de los patrones de diseño estructurales (como el patrón de adaptador, el patrón de puente, el patrón compuesto) y utiliza clases abstractas o interfaces con composición para implementarlo.

Patrón de diseño Decorator

Usamos la herencia o composición para extender el comportamiento de un objeto, pero esto se hace en tiempo de compilación y es aplicable a todas las instancias de la clase. No podemos agregar ninguna nueva funcionalidad ni eliminar ningún comportamiento existente en tiempo de ejecución, es aquí donde entra en juego el patrón Decorador. Supongamos que queremos implementar diferentes tipos de autos: podemos crear una interfaz Car para definir el método assemble y luego podemos tener un auto básico, además podemos extenderlo a un auto deportivo y un auto de lujo. La jerarquía de implementación se verá como en la siguiente imagen. Pero si queremos obtener un auto en tiempo de ejecución que tenga tanto las características de un auto deportivo como las de un auto de lujo, la implementación se vuelve compleja y, si además queremos especificar qué características se deben agregar primero, se vuelve aún más compleja. Ahora imagina si tenemos diez tipos diferentes de autos, la lógica de implementación usando herencia y composición sería imposible de gestionar. Para resolver este tipo de situación de programación, aplicamos el patrón decorador en Java. Necesitamos tener los siguientes tipos para implementar el patrón de diseño decorador.

  1. Interfaz del componente – La interfaz o clase abstracta que define los métodos que se implementarán. En nuestro caso, Car será la interfaz del componente.

    paquete com.journaldev.design.decorator;
    
    public interface Car {
    
    	public void ensamblar();
    }
    
  2. Implementación del componente – La implementación básica de la interfaz del componente. Podemos tener la clase BasicCar como nuestra implementación del componente.

    paquete com.journaldev.design.decorator;
    
    public class BasicCar implements Car {
    
    	@Override
    	public void ensamblar() {
    		System.out.print("Automóvil básico.");
    	}
    
    }
    
  3. Decorador – La clase Decorador implementa la interfaz del componente y tiene una relación TIENE-UNA con la interfaz del componente. La variable del componente debe ser accesible para las clases decoradoras hijas, por lo que haremos que esta variable sea protegida.

    paquete com.journaldev.design.decorator;
    
    public class CarDecorator implements Car {
    
    	protected Car car;
    	
    	public CarDecorator(Car c){
    		this.car=c;
    	}
    	
    	@Override
    	public void assemble() {
    		this.car.assemble();
    	}
    
    }
    
  4. Decoradores concretos – Extienden la funcionalidad básica del decorador y modifican el comportamiento del componente en consecuencia. Podemos tener clases de decoradores concretos como LuxuryCar y SportsCar.

    paquete com.journaldev.design.decorator;
    
    public class SportsCar extends CarDecorator {
    
    	public SportsCar(Car c) {
    		super(c);
    	}
    
    	@Override
    	public void assemble(){
    		super.assemble();
    		System.out.print(" Agregando características de un coche deportivo.");
    	}
    }
    
    paquete com.journaldev.design.decorator;
    
    public class LuxuryCar extends CarDecorator {
    
    	public LuxuryCar(Car c) {
    		super(c);
    	}
    	
    	@Override
    	public void assemble(){
    		super.assemble();
    		System.out.print(" Agregando características de un coche de lujo.");
    	}
    }
    

Patrón de diseño de decorador – Diagrama de clases

Programa de prueba del patrón de diseño de decorador

package com.journaldev.design.test;

import com.journaldev.design.decorator.BasicCar;
import com.journaldev.design.decorator.Car;
import com.journaldev.design.decorator.LuxuryCar;
import com.journaldev.design.decorator.SportsCar;

public class DecoratorPatternTest {

	public static void main(String[] args) {
		Car sportsCar = new SportsCar(new BasicCar());
		sportsCar.assemble();
		System.out.println("\n*****");
		
		Car sportsLuxuryCar = new SportsCar(new LuxuryCar(new BasicCar()));
		sportsLuxuryCar.assemble();
	}

}

Observa que el programa cliente puede crear diferentes tipos de objetos en tiempo de ejecución y también puede especificar el orden de ejecución. La salida del programa de prueba anterior es:

Basic Car. Adding features of Sports Car.
*****
Basic Car. Adding features of Luxury Car. Adding features of Sports Car.

Patrón de diseño de decorador – Puntos importantes

  • El patrón de diseño de decorador es útil para proporcionar capacidades de modificación en tiempo de ejecución y, por lo tanto, más flexibilidad. Es fácil de mantener y ampliar cuando hay más opciones disponibles.
  • La desventaja del patrón de diseño de decorador es que utiliza muchos objetos similares (decoradores).
  • El patrón de decorador se utiliza mucho en las clases de Java IO, como FileReader, BufferedReader, etc.

Source:
https://www.digitalocean.com/community/tutorials/decorator-design-pattern-in-java-example