English English

Patrón de Java - Decorador - Extiende la funcionalidad de tus objetos dinámicamente de una manera estructurada

Este patrón se utiliza para ampliar la funcionalidad de los objetos sin utilizar la herencia o cambiar el código original. Esto es parte de la serie "Patrón de Diseño en Java".

¿Qué es un decorador?

Un decorador es un patrón de diseño (utilizado en la programación en general) para ampliar la funcionalidad de un objeto creando un objeto de envoltura.
Este objeto de envoltura que se llama decorador se utiliza para envolver el objeto original que debe ser extendido.

El objeto decorador tiene las mismas funciones que el objeto original que se envuelve. Pero también puede extenderlo con funciones específicas que son usadas por este decorador.
La funcionalidad del objeto decorador se agrega en el tiempo de ejecución de su aplicación.

Un diagrama UML de una aplicación que utiliza el patrón de diseño "Decorador".

La interfaz "Burger": Esta interfaz también se llama "Componente" y define los métodos que se implementarán por clase que se añaden posteriormente.
La clase "ChickenBurger": Esta clase es la implementación del "Componente". Puede haber varias clases que implementan el "Componente" también.
La clase abstracta "BurgerDecorator": Este es el decorador que decora nuestro objeto original Burger y que contiene el objeto "Componente". Los objetos de instancia definida en esta clase son accedidos por las clases de niños.
La clase "Exotic", "Mozarella" y "Vegetables": Estos son los decoradores del hormigón. Ellos implementan la funcionalidad de la decoración y pueden ser extendidos con su respectiva funcionalidad.

 

Un ejemplo de aplicación

Este es un ejemplo de aplicación llamado "TastyBurgers" que es la realización del diagrama UML anterior.
Con la ayuda de este ejemplo obtendrá una comprensión más práctica de este patrón de diseño.

 

a. Interfaz del componente

public interface Burger {
	String getName();
	Integer getToppingsAmount();
}

Todos los métodos que están al menos disponibles para una hamburguesa se definen aquí.

 

b. Implementación del Componente

public class ChickenBurger implements Burger {	
	
	public ChickenBurger() {
	}
	
	@Override
	public String getName() {
		return "Standard Chicken Burger";
	}

	@Override
	public Integer getToppingsAmount() {
		return 0;
	}

}

 

c. Clase de decorador

public abstract class BurgerDecorator implements Burger{

	protected Burger burger;
	
	public BurgerDecorator(Burger b) {
		this.burger=b;
	}
	
	@Override
	public String getName() {
		return burger.getName();
	}

	@Override
	public Integer getToppingsAmount() {
		return burger.getToppingsAmount();
	}
	
}

Esta clase abstracta implementa la interfaz definida de un Burger.

 

d. Decoradores concretos

public class Exotic extends BurgerDecorator{

	public Exotic(Burger b) {
		super(b);
	}
	
	@Override
	public String getName() {
		return "Exotic Flavour Edition";
	}

	@Override
	public Integer getToppingsAmount() {
		return 5;
	}
	
	//Extended functionality - Only available in an Exotic Burger
	public Integer getSpicinessLevel() {
		return 10;
	}

}
public class Mozarella extends BurgerDecorator{
	
	public Mozarella(Burger b) {
		super(b);
	}
	
	@Override
	public String getName() {
		return "Mozarella Cheese";
	}

	@Override
	public Integer getToppingsAmount() {
		return 2;
	}

}
public class Vegetables extends BurgerDecorator {

	public Vegetables(Burger b) {
		super(b);
	}

	@Override
	public String getName() {
		return "Healthy Vegetables Edition";
	}

	@Override
	public Integer getToppingsAmount() {
		return 4;
	}
}

Las extensiones de la funcionalidad se añaden aquí.

Los decoradores creados pueden ser llamados en este formato:

public class TastyBurgers {

	public static void main(String[] args) {
		Burger burgerA = new Mozarella(new ChickenBurger());
		Burger burgerB = new Exotic(new ChickenBurger());
		Burger burgerC = new Vegetables(new ChickenBurger());
		System.out.println("Burger A Name: " + burgerA.getName() + " - Toppings No.: " + burgerA.getToppingsAmount());
		System.out.println("Burger B Name: " + burgerB.getName() + " - Toppings No.: " + burgerB.getToppingsAmount());
		System.out.println("Burger C Name: " + burgerC.getName() + " - Toppings No.: " + burgerC.getToppingsAmount());
		//The function which was extended (class: Exotic) can be accessed only if you define an object directly with the class name "Exotic":
		Exotic burgerBExtended = new Exotic(new ChickenBurger());
		System.out.println("Burger B Extended - Name: " + burgerBExtended.getName() + " - Toppings No.: " + burgerBExtended.getToppingsAmount() + " - Spiciness Level: " + burgerBExtended.getSpicinessLevel());

	}
}

Resultado:

Burger A Name: Mozarella Cheese - Toppings No.: 2
Burger B Name: Exotic Flavour Edition - Toppings No.: 5
Burger C Name: Healthy Vegetables Edition - Toppings No.: 4
Burger B Extended - Name: Exotic Flavour Edition - Toppings No.: 5 - Spiciness Level: 10

Si quieres usar una funcionalidad extendida en un decorador concreto, entonces tienes que declarar el objeto directamente sin la clase de interfaz.

Este fue un ejemplo de un decorador. Si programas en Java, entonces encontrarás regularmente clases de Java que usan el patrón del decorador. Por ejemplo BufferedInputStream o BufferedOutputStream.

 

Esta aplicación (proyecto) en Github:

https://github.com/a-dridi/decorator-tastyburgers

 

Usamos cookies en nuestro sitio web. Algunas de ellas son esenciales para el funcionamiento del sitio, mientras que otras nos ayudan a mejorar el sitio web y también la experiencia del usuario (cookies de rastreo). Puedes decidir por ti mismo si quieres permitir el uso de las cookies. Ten en cuenta que si las rechazas, puede que no puedas usar todas las funcionalidades del sitio web.