Menu
Coddy logo textTech

Patrón Decorator

Parte de la sección Programación Orientada a Objetos del Journey de GO de Coddy. Lección 93 de 107.

El patrón Decorator te permite añadir un comportamiento nuevo a los objetos de forma dinámica al envolverlos en otros objetos. Mientras que Adapter cambia una interfaz para que coincida con otra, Decorator mantiene la misma interfaz, pero mejora la funcionalidad al añadir capas de envoltorios alrededor del objeto original.

En Go, implementamos esto haciendo que tanto el tipo base como los decoradores implementen la misma interfaz:

type Notifier interface {
    Send(message string) string
}

type BasicNotifier struct{}

func (b BasicNotifier) Send(message string) string {
    return "Sending: " + message
}

Los decoradores envuelven otro Notifier y añaden comportamiento antes o después de delegar en él:

type TimestampDecorator struct {
    wrapped Notifier
}

func (t TimestampDecorator) Send(message string) string {
    timestamped := "[2024-01-15] " + message
    return t.wrapped.Send(timestamped)
}

type UppercaseDecorator struct {
    wrapped Notifier
}

func (u UppercaseDecorator) Send(message string) string {
    return strings.ToUpper(u.wrapped.Send(message))
}

El poder de Decorator reside en apilar varios envoltorios para combinar comportamientos:

notifier := BasicNotifier{}
withTimestamp := TimestampDecorator{wrapped: notifier}
withBoth := UppercaseDecorator{wrapped: withTimestamp}

fmt.Println(notifier.Send("Hello"))
// Enviando: Hello

fmt.Println(withBoth.Send("Hello"))
// ENVIANDO: [2024-01-15] HELLO

Decorator es ideal cuando necesitas añadir responsabilidades a los objetos sin modificar su código, especialmente cuando se necesitan diferentes combinaciones de características. Se usa habitualmente para capas de registro, almacenamiento en caché, autenticación y compresión.

challenge icon

Desafío

Fácil

¡Construyamos un sistema de pedidos para una cafetería usando el patrón Decorator! Crearás una bebida de café base y decoradores que añaden extras como leche, azúcar y crema batida. Cada decorador envuelve al anterior, construyendo tanto la descripción como el precio: ¡un ejemplo perfecto del mundo real de cómo los decoradores añaden funcionalidad por capas!

Organizarás tu código en tres archivos:

  • beverage.go: Define tu interfaz principal y el tipo de café base.

    Crea una interfaz Beverage con dos métodos:

    • Description() string: devuelve qué es la bebida
    • Cost() float64: devuelve el precio

    Implementa una estructura Coffee como tu bebida base. Su descripción debe ser Coffee y su costo debe ser 2.00.

  • decorators.go: Crea tus tipos de decoradores que añadan extras a cualquier bebida.

    Cada decorador envuelve un Beverage e implementa la misma interfaz. Crea tres decoradores:

    • MilkDecorator: añade , Milk a la descripción y 0.50 al costo
    • SugarDecorator: añade , Sugar a la descripción y 0.25 al costo
    • WhipDecorator: añade , Whip a la descripción y 0.75 al costo

    Cada decorador debe llamar a los métodos de la bebida envuelta y mejorar el resultado.

  • main.go: Crea bebidas personalizadas apilando decoradores.

    Lee una cantidad de extras que se añadirán. Después, para cada extra, lee su tipo (milk, sugar o whip) y envuelve tu bebida actual con el decorador correspondiente.

    Después de aplicar todos los extras, imprime la descripción y el costo finales de la bebida en líneas separadas. Da formato al costo como $X.XX con exactamente dos decimales.

Se proporcionarán las siguientes entradas:

  • Línea 1: cantidad de extras que se añadirán
  • Líneas siguientes: un tipo de extra por línea (milk, sugar o whip)

Por ejemplo, dada la siguiente entrada:

2
milk
sugar

Tu salida debe ser:

Coffee, Milk, Sugar
$2.75

Y dada la siguiente entrada:

3
whip
milk
milk

Tu salida debe ser:

Coffee, Whip, Milk, Milk
$3.75

Y dada la siguiente entrada:

0

Tu salida debe ser:

Coffee
$2.00

Observa cómo cada decorador envuelve la bebida anterior, construyendo tanto la descripción como el costo. Puedes añadir el mismo extra varias veces (como leche doble), y el orden de los decoradores determina el orden en la descripción. El café base no sabe nada sobre los extras: ¡cada decorador simplemente mejora aquello que envuelve!

Pruébalo tú mismo

package main

import (
	"fmt"
)

func main() {
	// Lee el número de extras
	var count int
	fmt.Scanln(&count)

	// Empieza con un café base
	var drink Beverage = &Coffee{}

	// Lee cada extra y envuelve la bebida con el decorador apropiado
	for i := 0; i < count; i++ {
		var extra string
		fmt.Scanln(&extra)

		// TODO: Basado en el tipo de extra, envuelve drink con el decorador apropiado
		// Usa MilkDecorator para "milk"
		// Usa SugarDecorator para "sugar"
		// Usa WhipDecorator para "whip"
	}

	// TODO: Imprime la descripción final y el costo
	// Formatea el costo como $X.XX con exactamente dos decimales
}
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación Orientada a Objetos

Practica por tu cuenta: Compilador de Go online