En este artículo veremos el patrón Decorator (perteneciente a la familia de patrones estructurales). Crearemos un ejemplo de aplicación de este patrón Decorator en typescript para Angular.

Con este patrón Decorator, podremos añadir dinámicamente funcionalidades a un objeto, constituyendo una alternativa a la herencia de clases.

Patrón Decorator

Para entender mejor este concepto, vamos a pensar en un ejemplo de un programa de facturación de una empresa de gimnasios como podría ser de una compañía de servicios (como la eléctrica, de agua, gas ….), o cualquier organización que facture a clientes.

Empezaremos con que se factura a un “abonado” (una persona física) que se ha apuntado al gimnasio. Pueden haber diferentes tipos de abonados: VIP (trabajadores del propio gimnasio, enchufados, …), de empresa (por algún convenio colectivo) ó individuales (el resto de los mortales). A partir de esta base, cada abonado podrá seleccionar una serie de “complementos” como puede ser el de disponer de una toalla grande, una toalla pequeña, y servicio de lavandería.

Se puede rebuscar más pero el concepto es poder añadir funcionalidad a una clase, en este caso, añadiendo unos “complementos”.

Si esto lo hiciéramos medianos la herencia, podríamos crear una clase abonado y extender esta clase para cada tipo (VIP, empresa y normal) luego para cada complemento seguiríamos extendiendo cada clase: VipBigTowel, VipLittleTowel, VipLaundry. ¿Y si quiero tener lavandería y toalla grande? pues una clase VipBigTowelLaundry. ¿Y si además la toalla pequeña? pues VipBigTowelLittleTowelLaundry … y así con todas las combinaciones…Mal vamos!!! Tenemos una explosión de clases. Bueno, entonces podemos poner una lista con todos esos complementos, ¿no? El problema es que podríamos añadir entonces un complemento a una bicicleta estática…

Lo que se pretende al final, es que cada complemento envuelva al abonado añadiéndole esa característica para que siga siendo un abonado.

Manos al teclado!

Para empezar crearemos una clase abstracta llamada “SubscriberTemplate” que será una clase abstracta que heredarán todos los tipos de abonados. Así tendremos una clase “VipSubscriber”, NormalSubscriber” y “CompanySubscriber” que tendrán una base inicial para pagar “amount” y añadimos la obligación de crear a las clases derivadas un método para devolver su nombre.

export abstract class SubscriberTemplate {
    private _amount: number = 0;
 
    public constructor(amount: number) {
        this._amount = amount;
    }
 
    public getAmount(): number {
        return this._amount;
    }
 
    public abstract getDescription(): string;
}

Ahora podemos crear las 3  clases de abonados, por ejemplo para el Vip:

import { SubscriberTemplate } from './subscriberTemplate';
 
export class VipSubscriber extends SubscriberTemplate {
 
    public constructor(amount: number) {
        super(amount);
    }
 
    public getDescription(): string {
        return 'Vip Subscriber';
    }
}

Ahora es cuando vamos a aplicar la decoración, es decir, crear las clases para cada complemento. Cada complemento heredará la siguiente clase abstracta para no repetir código (la cual a su vez hereda de la clase SubscriberTemplate). Quedando esta clase abstracta para los complementos de la siguiente manera:

export class ComplementTemplate extends SubscriberTemplate {
    private _subscriber: SubscriberTemplate;
 
    public constructor(subscriber: SubscriberTemplate, amount: number) {
        super(amount);
        this._subscriber = subscriber;
    }
 
    public getAmount(): number {
        return this._subscriber.getAmount() + super.getAmount();
    }
 
    public  getDescription(): string {
        return this._subscriber.getDescription();
    }
}

En el constructor de esta clase abstracta para los complementos le llegará un abonado así como el importe del complemento.

Para obtener un importe final, supondremos que cada complemento se suma al importe base del abonado. Y la descripción será la del abonado.

Ahora podemos empezar a ir creando las siguientes clases para cada complemento, así para una toalla grande tendremos:

export class BigTowelComplement extends ComplementTemplate {
 
    public constructor(subscriber: SubscriberTemplate, amount: number) {
        super(subscriber, amount);
    }
 
    public  getDescription(): string {
        return `${super.getDescription()} with Big Towel`;
    }
 
}

Uso

Ahora podemos ya utilizar las clases para ir decorando un Abonado. Por ejemplo:

export class AppComponent implements OnInit {
  public title: string = 'decorator-pattern';
  public subscriber: SubscriberTemplate;
 
  public ngOnInit(): void {
    this.subscriber = new VipSubscriber(200)
    this.subscriber = new LaundryComplement(this.subscriber, 100);
    this.subscriber = new BigTowelComplement(this.subscriber, 10);
  }
 
}

También se podría crear en vez de complementos pues algo parecido con descuentos, promociones, … y habría que cambiar la política de precios, es decir, en vez de sumar como hacen los complementos pues ir aplicando descuentos.

Espero que pueda ser útil utilizar este patrón en los diferentes desarrollos. ¿Qué te ha parecido?

Puedes descargar el código del artículo en GitHub