Decorar um objeto de uma determinada classe para obtermos resultados diferenciados no ciclo de vida do nosso sistema. Quando eu digo decorar não é criar varias classes herdando de uma classe abstrata e em tempo de execução criar e instancia essas classes, o Decorate nos permite criar classes decorativas que usaremos em tempo de execução para alterarmos o comportamento e/ou resultado de um objeto de uma determinada classe. 

Vamos a um estabelecimento onde são vendidas algumas bebidas, café, leite, chá e pingado, analisando a solução já existente, vimos que existe uma classe café, uma leite, uma chá e uma pingado que contém uma instancia da classe café e uma instancia da classe leite.

#region [Bebidas]

 

    public abstract class Bebida

    {

        public virtual String Descricao { get; set; }

        public virtual Double Custo { get; set; }

    }

 

 

    public class Cafe : Bebida

    {

        public Cafe()

        {

            Custo = 1.50;

            Descricao = string.Format("Café \t\t1x{0:C} \t{0:C}", Custo);

        }

    }

 

    public class Leite : Bebida

    {

        public Leite()

        {

            Custo = 1.80;

            Descricao = string.Format("Leite \t\t1x{0:C}\t{0:C}", Custo);

        }

    }

 

    public class Cha : Bebida

    {

        public Cha()

        {

            Custo = 1.45;

            Descricao = string.Format("Chá \t\t1x{0:C}\t{0:C}", Custo);

        }

    }

 

    public class Pingado : Bebida

    {

        private Leite _leite = new Leite();

        private Cafe _Cafe = new Cafe();

 

        public Pingado()

        {

            Custo = _Cafe.Custo + _leite.Custo;

            Descricao = string.Format("Pingado \n{0} \n{1}", _Cafe.Descricao, _leite.Descricao);

        }

    }

 

    #endregion

A classe pingado é um sinal que a estrutura está faltando flexibilidade para estendermos as possibilidades de combinações e caso o estabelecimento queira servir leite com chocolate, ou café com menta, da maneira que está hoje teremos que criar varias classes para atendermos essa necessidade.

Ou seja, se queremos colocar menta em todas as bebidas teremos que criar uma classe para cada bebida ficando cafeComMenta, LeitaComMenta e ChaComMenta, imagina se tivéssemos que criar cinco condimentos (Creme, Chocolate, Canela, Menta e Caramelo) terá que criar mais quinze classes, e pensando que poderemos ter combinações do tipo: Leite com Canela e chocolate, a nossa vida seria só criar classes não é verdade?

Bom, analisamos a situação e descobrimos que deveremos mudar um pouco esse cenário, pois não é produto e é fechado para extensão e muito aberto para modificação e isso não é bom.

Existe um principio de projeto que diz: As classes devem estar abertas para extensão e fechadas para modificação.

Pois bem, decidimos não mexer nas classes de bebidas, e sim criarmos classes que vão entender as classes já existentes, de forma a decorar essas classes.

Opa! Decorar uma classe acho que eu já li algo sobre decorar uma classe hoje. Isso mesmo com Decorate pode dar novas características as nossas classes.

Mas vamos parar de tento blábláblá e vamos ver como criar nossos decoradores.

Vamos pegar as classes que já existe no nosso cliente, que são as bebidas que ele trabalha e vamos criar nossas classes decoradoras.

Primeiro criaremos uma classe abstrata para nossos decoradores.

    public abstract class Decorador : Bebida

    {

        protected Double _CustoDecoracao;

        protected int _quantidade = 1;

    }

Notem que essa classe herda da classe bebida igual todas as bebidas já criadas logo mais verão por que.

A seguir vamos criar as classes decoradoras.

public class Creme : Decorador

    {

        private Bebida _bebida;

 

        public Creme(Bebida bebida)

        {

            _bebida = bebida;

            _CustoDecoracao = 0.50;

        }

 

        public Creme(Bebida bebida, int quantidade)

            : this(bebida)

        {

            _quantidade = quantidade;

        }

 

        public override string Descricao

        {

            get

            {

                return string.Format("{0} \nCreme \t\t{2}x{1:C} \t{3:C}", _bebida.Descricao, _CustoDecoracao, _quantidade, _CustoDecoracao * _quantidade);

            }

        }

 

        public override double Custo

        {

            get

            {

                return _CustoDecoracao + _bebida.Custo;

            }

        }

    }

 

    public class Chocolate : Decorador

    {

        private Bebida _bebida;

 

        public Chocolate(Bebida bebida)

        {

            _bebida = bebida;

            _CustoDecoracao = 0.30;

        }

 

        public Chocolate(Bebida bebida, int quantidade)

            : this(bebida)

        {

            _quantidade = quantidade;

        }

 

        public override string Descricao

        {

            get

            {

                return string.Format("{0} \nChocolate \t{2}x{1:C} \t{3:C}", _bebida.Descricao, _CustoDecoracao, _quantidade, _CustoDecoracao * _quantidade);

            }

        }

 

        public override double Custo

        {

            get

            {

                return _CustoDecoracao + _bebida.Custo;

            }

        }

    }

 

    public class Canela : Decorador

    {

        private Bebida _bebida;

 

        public Canela(Bebida bebida)

        {

            _bebida = bebida;

            _CustoDecoracao = 0.10;

        }

 

        public Canela(Bebida bebida, int quantidade)

            : this(bebida)

        {

            _quantidade = quantidade;

        }

 

        public override string Descricao

        {

            get

            {

                return string.Format("{0} \nCanela \t\t{2}x{1:C} \t{3:C}", _bebida.Descricao, _CustoDecoracao, _quantidade, _CustoDecoracao * _quantidade);

            }

        }

 

        public override double Custo

        {

            get

            {

                return _CustoDecoracao + _bebida.Custo;

            }

        }

    }

 

    public class Menta : Decorador

    {

        private Bebida _bebida;

 

        public Menta(Bebida bebida)

        {

            _bebida = bebida;

            _CustoDecoracao = 0.15;

        }

 

        public Menta(Bebida bebida, int quantidade)

            : this(bebida)

        {

            _quantidade = quantidade;

        }

 

        public override string Descricao

        {

            get

            {

                return string.Format("{0} \nMenta \t\t{2}x{1:C} \t{3:C}", _bebida.Descricao, _CustoDecoracao, _quantidade, _CustoDecoracao * _quantidade);

            }

        }

 

        public override double Custo

        {

            get

            {

                return _CustoDecoracao + _bebida.Custo;

            }

        }

    }

 

    public class Caramelo : Decorador

    {

        private Bebida _bebida;

 

        public Caramelo(Bebida bebida)

        {

            _bebida = bebida;

            _CustoDecoracao = 0.25;

        }

 

        public Caramelo(Bebida bebida, int quantidade)

            : this(bebida)

        {

            _quantidade = quantidade;

        }

 

        public override string Descricao

        {

            get

            {

                return string.Format("{0} \nCaramelo \t{2}x{1:C} \t{3:C}", _bebida.Descricao, _CustoDecoracao, _quantidade, _CustoDecoracao * _quantidade);

            }

        }

 

        public override double Custo

        {

            get

            {

                return _CustoDecoracao + _bebida.Custo;

            }

        }

    }

E ai estão elas, analisar mais de perto, nossas classes herdam de Decorador que herda de Bebidas, então temos nos decoradores todas as características de uma bebida mais as características de um decorador, certo?

Todo decorador tem um objeto privado da classe Bebida, ou seja, ele consegue encapsular qualquer objeto que herda Bebida, assim consegue encapsular um Café ou um Chocolate. Esse é o real motivo da classe Decorador herdar Bebida, pois podemos fazer qualquer combinação.

Esse padrão não é complicado e muito útil, pois imagina, a classe pingado é nada mais nada menos que café com leite, visto que ainda não podemos combinar os dois pois assumimos que não vamos alterar as classes existentes justamente para não corrermos o risco de gerarmos erros no que já está em funcionamento mais poderíamos criar uma classe Decoradora café e um outra leite, onde poderíamos decorar o café com leite e o leite com café.

Vamos ao nosso teste.

class Program

{

    static void Main(string[] args)

    {

        Bebida b1 = new Cafe();

        b1 = new Creme(b1);

        b1 = new Chocolate(b1);

        b1 = new Canela(b1);

        b1 = new Menta(b1);

        b1 = new Caramelo(b1);

        Console.WriteLine("{0} \n\t\t\tTotal \t{1:C}\n", b1.Descricao, b1.Custo);

 

        b1 = new Cha();

        b1 = new Creme(b1);

        b1 = new Chocolate(b1);

        b1 = new Canela(b1);

        b1 = new Menta(b1);

        b1 = new Caramelo(b1);

        Console.WriteLine("{0} \n\t\t\tTotal \t{1:C}\n", b1.Descricao, b1.Custo);

 

        b1 = new Leite();

        b1 = new Creme(b1);

        b1 = new Chocolate(b1);

        b1 = new Canela(b1);

        b1 = new Menta(b1);

        b1 = new Caramelo(b1);

        Console.WriteLine("{0} \n\t\t\tTotal \t{1:C}\n", b1.Descricao, b1.Custo);

 

        b1 = new Pingado();

        b1 = new Creme(b1);

        b1 = new Chocolate(b1);

        b1 = new Canela(b1);

        b1 = new Menta(b1);

        b1 = new Caramelo(b1);

        Console.WriteLine("{0} \n\t\t\tTotal \t{1:C}\n", b1.Descricao, b1.Custo);

 

        Console.ReadKey();

    }

}

E ai está todas as bebidas combinadas com todos os condimentos, poderíamos ter feito um leite com menta, ou um café com creme, isso não importa, o que importa é Fechado para alteração e aberto para extensão.

 

Chegamos ao fim deste artigo, espero novamente ter conseguido passar a idéia desse conceito. Até a próxima, ahh não se esqueçam de fazer o download do exemplo.

 

Fontes:

FREEMAN, ERIC & FREEMAN, ELISABETH – Use a Cabeça! Padrões de Projetos (Design Patterns), 2ª Edição [http://www.livrariasaraiva.com.br/produto/1995765/]