Patrón de diseño: DECORATOR

Hoy vamos a ver otro patrón estructural que es muy útil cuando creamos una aplicación. El patrón decorador nos ayuda a extender el comportamiento de una clase como veremos a continuación.

SwiftBeta

Tabla de contenido


👇 SÍGUEME PARA APRENDER SWIFTUI, SWIFT, XCODE, etc 👇

El patrón Decorator es otro patrón estructural. Es muy útil pasa modificar o añadir comportamiento a las clases cuando no queremos heredar de ellas porque acabaríamos teniendo un caos de subclases. Para este caso usamos composición.

Un ejemplo muy sencillo, imagínate que tu backend está de guardia, y quiere recibir avisos por distintos canales en caso de tener un fallo en los servidores.
Nuestro sistema de alarmas nos permite enviar alertas por SMS, llamada telefónica, email, Slack, etc.

¿Cómo podríamos crear un sistema de alarmas? Podemos tener una clase base que sea una Alerta con el tipo de alerta básica que en nuestro caso es enviar un email. A partir de ahí, podemos crear distintas subclases para SMS, llamada telefónica, email, Slack etc. y cada una sobrescribe cómo quiere enviar esta alerta. En el caso de Slack llamaría a la API de Slack, en el caso de email enviaría un email, etc

Hasta aquí todo bien, pero imagínate que viene el CTO y te dice que quiere que lleguen SMS y email, o por Slack y email (es decir, tener una combinación de alertas que puedan variar en el tiempo).
En lugar de crear varias subsclases con estas combinaciones podemos simplificar mucho código (y tiempo del programador) usando el patrón Decorator.

Para ello podemos hacer lo siguiente:

protocol Alertable {
    var name: String { get }
    func sendMessage() -> String
}

class AlertBase: Alertable {
    var name: String {
        "Base_Decorator"
    }
        
    func sendMessage() -> String {
        print("Send Alert message \(self.name)")
        return "Default"
    }
}

Vamos a crear una clase base que conforma un protocolo. Este será el caso de nuestra alerta más básica.
La función sendMessage() enviaría un mensaje vía email. Ahora vamos a crear distintos decorators para añadir distintos sistemas de alerta:

protocol AlertableType: Alertable {
    var base: Alertable { get }
}

// Decorators

class SMSDecorator: AlertableType {
    var name: String {
        "SMS_Decorator"
    }
    
    let base: Alertable
    
    init(base: Alertable) {
        self.base = base
    }
    
    func sendMessage() -> String {
        print("Send Alert message \(self.name)")
        return self.base.sendMessage()
    }
}

class SlackDecorator: AlertableType {
    var name: String {
        "Slack_Decorator"
    }
    
    let base: Alertable
    
    init(base: Alertable) {
        self.base = base
    }
    
    func sendMessage() -> String {
        print("Send Alert message \(self.name)")
        return self.base.sendMessage()
    }
}

En este caso hemos creado 2 decorators que enviarán alertas por SMS y por Slack. Para ello hemos creado un nuevo protocolo llamado AlertableType.
Este protocolo tiene una propiedad llamada base que conforma el protocolo Alertable, es por eso que ahora podemos ir anidando distintos decorators para añadir comportamiento a nuestra alerta básica (la que sólo enviaba un email).

Podemos hacer lo siguiente:

let base = AlertBase()
let smsAlert = SMSDecorator(base: base)
let slackAlert = SlackDecorator(base: smsAlert)

slackAlert.sendMessage()

Al ejecutar las líneas en nuestro playground, obtenemos el siguiente resultado:

Send Alert message Slack_Decorator
Send Alert message SMS_Decorator
Send Alert message Base_Decorator

Esto significa que hemos creado nuestra alerta básica, y la hemos pasado al SMSDecorator, y luego hemos pasado la instancia de SMSDecorator a SlackDecorator. Finalmente ejecutamos el método sendMessage() y ejecutaría en cadena todos los decorators hasta llegar a la clase de la alerta básica.

Un ejemplo muy simple es, imagínate que tienes frío y quieres ponerte un abrigo, luego te pones un gorro, luego unos guantes, etc. Te vas decorando tu mismo con prendas de ropa.

Hasta aquí el post de hoy, gracias por leernos! 🤓
Si tienes preguntas no dudes en contactar con nosotros a través de Twitter