Menu
Coddy logo textTech

Padrão Decorator

Parte da seção Programação Orientada a Objetos do Journey de C# da Coddy. Lição 58 de 70.

O padrão Decorator é um padrão estrutural que permite anexar novos comportamentos a objetos envolvendo-os em objetos especiais chamados decoradores. Diferentemente da herança, que adiciona comportamento em tempo de compilação, os decoradores adicionam funcionalidades dinamicamente em tempo de execução, sem modificar a classe original.

O padrão funciona fazendo com que os decoradores implementem a mesma interface que o objeto que envolvem. Cada decorador mantém uma referência ao objeto envolvido e delega as chamadas a ele, adicionando seu próprio comportamento antes ou depois:

public interface ICoffee
{
    string GetDescription();
    decimal GetCost();
}

public class SimpleCoffee : ICoffee
{
    public string GetDescription() => "Coffee";
    public decimal GetCost() => 2.00m;
}

public abstract class CoffeeDecorator : ICoffee
{
    protected ICoffee _coffee;
    
    public CoffeeDecorator(ICoffee coffee) => _coffee = coffee;
    
    public virtual string GetDescription() => _coffee.GetDescription();
    public virtual decimal GetCost() => _coffee.GetCost();
}

public class MilkDecorator : CoffeeDecorator
{
    public MilkDecorator(ICoffee coffee) : base(coffee) { }
    
    public override string GetDescription() => _coffee.GetDescription() + ", Milk";
    public override decimal GetCost() => _coffee.GetCost() + 0.50m;
}

O poder dos decoradores está na capacidade de serem empilhados. Você pode envolver um objeto decorado com outro decorador:

ICoffee coffee = new SimpleCoffee();
coffee = new MilkDecorator(coffee);
coffee = new MilkDecorator(coffee);  // Leite duplo!

Console.WriteLine(coffee.GetDescription());  // Café, Leite, Leite
Console.WriteLine(coffee.GetCost());         // 3.00

O padrão Decorator é ideal quando você precisa adicionar responsabilidades a objetos sem criar uma explosão de subclasses. Cada decorador se concentra em uma única responsabilidade, e você os combina conforme necessário para construir exatamente o comportamento desejado.

challenge icon

Desafio

Fácil

Vamos criar um sistema de pedidos de pizza usando o padrão Decorator. Você criará uma pizza base e, em seguida, adicionará coberturas dinamicamente, com cada cobertura envolvendo a camada anterior e adicionando algo tanto à descrição quanto ao preço.

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

  • Pizza.cs: Defina uma interface IPizza no namespace PizzaShop com dois métodos: GetDescription(), que retorna uma string, e GetPrice(), que retorna um decimal. Em seguida, crie uma classe PlainPizza que implemente essa interface — ela deve retornar "Pizza" como descrição e 8.00m como preço base.
  • ToppingDecorators.cs: Crie uma classe abstrata ToppingDecorator que implemente IPizza e mantenha uma referência à pizza envolvida (passada pelo construtor). Seus métodos virtuais devem delegar para a pizza envolvida. Em seguida, crie três decorators concretos:
    • CheeseTopping — acrescenta ", Cheese" à descrição e adiciona 1.50m ao preço
    • PepperoniTopping — acrescenta ", Pepperoni" à descrição e adiciona 2.00m ao preço
    • MushroomTopping — acrescenta ", Mushrooms" à descrição e adiciona 1.25m ao preço
  • Program.cs: Reúna tudo começando com uma pizza simples e envolvendo-a com coberturas com base na entrada. Cada decorator de cobertura envolve a pizza anterior, construindo o pedido final camada por camada.

Você receberá uma entrada:

  • Uma lista de coberturas separadas por vírgulas para adicionar (por exemplo, cheese,pepperoni,cheese)

Comece com uma PlainPizza e, em seguida, envolva-a com o decorator apropriado para cada cobertura, na ordem em que aparecem. As coberturas válidas são cheese, pepperoni e mushrooms. Depois de aplicar todas as coberturas, imprima a descrição em uma linha e o preço total na linha seguinte.

Por exemplo, se a entrada for cheese,pepperoni,mushrooms, a saída deverá ser:

Pizza, Cheese, Pepperoni, Mushrooms
12.75

Observe como você pode adicionar a mesma cobertura várias vezes! Se a entrada for cheese,cheese, você obterá:

Pizza, Cheese, Cheese
11.00

Isso demonstra a flexibilidade do padrão decorator — cada decorator envolve qualquer pizza que recebe, seja uma pizza simples ou uma que já tenha decorators, permitindo empilhar comportamentos dinamicamente!

Experimente você mesmo

using System;
using PizzaShop;

class Program
{
    public static void Main(string[] args)
    {
        // Lê a lista de coberturas separada por vírgulas
        string input = Console.ReadLine();
        string[] toppings = input.Split(',');
        
        // TODO: Comece com uma PlainPizza
        
        // TODO: Percorra cada cobertura e envolva a pizza
        // com o decorador apropriado baseado no nome da cobertura:
        // - "cheese" -> CheeseTopping
        // - "pepperoni" -> PepperoniTopping
        // - "mushrooms" -> MushroomTopping
        
        // TODO: Imprima a descrição final e o preço
    }
}
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 C# online