Menu
Coddy logo textTech

Padrão Decorator

Parte da seção Programação Orientada a Objetos do Journey de GO da Coddy. Lição 93 de 107.

O padrão Decorator permite adicionar novos comportamentos aos objetos dinamicamente, envolvendo-os em outros objetos. Enquanto o Adapter altera uma interface para corresponder a outra, o Decorator mantém a mesma interface, mas aprimora a funcionalidade ao adicionar camadas de wrappers ao redor do objeto original.

Em Go, implementamos isso fazendo com que tanto o tipo base quanto os decoradores implementem a mesma interface:

type Notifier interface {
    Send(message string) string
}

type BasicNotifier struct{}

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

Os decoradores envolvem outro Notifier e adicionam comportamento antes ou depois de delegar a ele:

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))
}

O poder do Decorator está em empilhar vários wrappers para combinar comportamentos:

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

O Decorator é ideal quando você precisa adicionar responsabilidades aos objetos sem modificar o código deles, especialmente quando diferentes combinações de recursos são necessárias. Ele é comumente usado em camadas de registro, armazenamento em cache, autenticação e compactação.

challenge icon

Desafio

Fácil

Vamos criar um sistema de pedidos de uma cafeteria usando o padrão Decorator! Você criará uma bebida de café base e decoradores que adicionam extras como leite, açúcar e chantili. Cada decorador envolve o anterior, construindo tanto a descrição quanto o preço: um exemplo perfeito e realista de como os decoradores adicionam funcionalidades em camadas.

Você organizará seu código em três arquivos:

  • beverage.go: Defina sua interface principal e o tipo base de café.

    Crie uma interface Beverage com dois métodos:

    • Description() string: retorna o que é a bebida
    • Cost() float64: retorna o preço

    Implemente uma struct Coffee como sua bebida base. Sua descrição deve ser Coffee e seu custo deve ser 2.00.

  • decorators.go: Crie seus tipos de decoradores que adicionam extras a qualquer bebida.

    Cada decorador envolve um Beverage e implementa a mesma interface. Crie três decoradores:

    • MilkDecorator: adiciona , Milk à descrição e 0.50 ao custo
    • SugarDecorator: adiciona , Sugar à descrição e 0.25 ao custo
    • WhipDecorator: adiciona , Whip à descrição e 0.75 ao custo

    Cada decorador deve chamar os métodos da bebida envolvida e aprimorar o resultado.

  • main.go: Monte bebidas personalizadas empilhando decoradores.

    Leia uma quantidade de extras a serem adicionados. Em seguida, para cada extra, leia seu tipo (milk, sugar ou whip) e envolva sua bebida atual com o decorador apropriado.

    Depois de aplicar todos os extras, imprima a descrição e o custo finais da bebida em linhas separadas. Formate o custo como $X.XX, com exatamente duas casas decimais.

As seguintes entradas serão fornecidas:

  • Linha 1: Número de extras a serem adicionados
  • Linhas seguintes: Um tipo de extra por linha (milk, sugar ou whip)

Por exemplo, dada a entrada:

2
milk
sugar

Sua saída deve ser:

Coffee, Milk, Sugar
$2.75

E dada a entrada:

3
whip
milk
milk

Sua saída deve ser:

Coffee, Whip, Milk, Milk
$3.75

E dada a entrada:

0

Sua saída deve ser:

Coffee
$2.00

Observe como cada decorador envolve a bebida anterior, construindo tanto a descrição quanto o custo. Você pode adicionar o mesmo extra várias vezes (como leite duplo), e a ordem dos decoradores determina a ordem na descrição. O café base não sabe nada sobre os extras: cada decorador simplesmente aprimora aquilo que envolve!

Experimente você mesmo

package main

import (
	"fmt"
)

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

	// Começar com um café base
	var drink Beverage = &Coffee{}

	// Ler cada extra e envolver a bebida com o decorator apropriado
	for i := 0; i < count; i++ {
		var extra string
		fmt.Scanln(&extra)

		// TODO: Com base no tipo de extra, envolver drink com o decorator apropriado
		// Usar MilkDecorator para "milk"
		// Usar SugarDecorator para "sugar"
		// Usar WhipDecorator para "whip"
	}

	// TODO: Imprimir a descrição final e o custo
	// Formatar o custo como $X.XX com exatamente duas casas decimais
}
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Programação Orientada a Objetos

Pratique por conta própria: Compilador de Go online