Desenvolvimento

3 jan, 2019

Arquitetura e desenvolvimento de software – Parte 10: Decorator

Publicidade

Fala, galera! Tudo bem?

Hoje falaremos sobre o pattern Decorator!

Decorator

O padrão Decorator é muito utilizado quando queremos agregar funcionalidade, estado ou comportamento extra a um objeto em tempo de execução. É visto como uma alternativa à herança, pois é adicionada uma responsabilidade extra ao objeto, e não à classe.

Um exemplo interessante para podermos entender melhor seu conceito é imaginar a compra de passagens de avião. Podemos comprar a passagem, mas com adicionais, como incluir bagagem despachada, classe, alimentação diferente e outros itens. Imagine criar uma herança para cada caso de combinação. Isso seria inviável.

O padrão pode ser representado através do diagrama UML abaixo:

Temos os seguintes itens no diagrama:

  • Component é uma interface que define o contrato de nosso componente
  • ConcreteComponent é a implementação do componente, segundo contrato definido pela interface Component
  • Decorator é uma classe base que possui uma referência para o componente desejado. Essa classe implementa a interface de contrato do componente
  • ConcreteDecorator é a implementação concreta do decorator, que aplica novos comportamentos ou features, utilizando a referência do componente para o comportamento base

Se analisarmos friamente, é como uma parede onde aplicamos um papel de parede. Este contém a parede, dando-lhe uma nova aparência, mas tendo acesso à sua base.

E como podemos ter vários decorators utilizando a mesma referência de componente, temos diversas funcionalidades acrescentadas a um mesmo objeto, sem a necessidade de combinar N heranças diferentes, o que aumentaria muito a quantidade de implementações.

Vamos verificar as aplicabilidades e exemplos deste padrão na ficha resumo, ok?

Ficha resumo

  • Nome: Decorator
  • Objetivo/intenção: quando precisamos adicionar comportamentos, funcionalidades ou estado extra a um objeto, em tempo de execução
  • Motivação: quando temos casos em que diversos comportamentos e funcionalidades podem mudar durante tempo de execução, precisamos de um mecanismo viável para isso, já que compor heranças com todas as combinações de funcionalidades, dependendo do caso, pode gerar um número alto de implementações, tornando isso inviável
  • Aplicabilidade: o padrão Decorator é bastante utilizado quando queremos adicionar ou modificar funcionalidades em tempo de execução ou evitar o crescimento absurdo de código com heranças. É bastante similar ao padrão Composite, sendo considerado uma forma mais simples dele
  • Estrutura: abaixo temos a estrutura UML do pattern, descrita no livro do GoF. Component – uma interface que define o contrato de nosso componente. ConcreteComponent é a implementação do componente, segundo contrato definido pela interface Component. Decorator é uma classe base que possui uma referência para o componente desejado. Esta classe implementa a interface de contrato do componente e o ConcreteDecorator. é a implementação concreta do decorator, que aplica novos comportamentos ou features, utilizando a referência do componente para o comportamento base

  • Consequências: o Decorator resolve problemas que a herança gera em determinados momentos, em que classes precisam ser muitos específicas ou temos muitas combinações. Nestes momentos, o Decorator é aplicado, diminuindo drasticamente as classes geradas e permitindo flexibilidade aos atributos e métodos. Isto acontece, pois a solução por trás deste padrão é encapsular o objeto original dentro de uma interface. Esta solução traz ao projeto uma flexibilidade maior, em que pode se adicionar ou remover responsabilidades sem que seja necessário editar o código-fonte, alta coesão e fraco acoplamento
  • Implementações: abaixo temos um exemplo de código para a estrutura de classes descrita no diagrama em “Estrutura”
//Componente
public interface IPassagem
{
  void Embarcar();
}

//Componente Concreto
public sealed class Passagem : IPassagem
{
  public void Embarcar()
  {
    Console.WriteLine("Embarcando");
  }
}

//Decorador
public class PassagemComBagagem
{
  IPassagem passagem;
  
  public PassagemComBagagem(IPassagem p)
  {
    passagem = p;
  }
  
  public void Bagagem()
  {
    Console.WriteLine("Embarcando bagagem");
  }
  
  public void Embarcar()
  {
    passagem.Embarcar();
    Bagagem();
  }
}
  • Usos conhecidos: muito utilizado na definição dinâmica de funcionalidades, como no exemplo da emissão de passagens de avião, quando precisamos adicionar comportamentos, estado, em tempo de execução, sem gerar um aumento drástico na quantidade de classes e mantendo a solução simples
  • Padrões relacionados: Adapter, Strategy e Composite

Concluindo

Bom, pessoal. Este foi o padrão Decorator: bem útil quando precisamos de uma dinâmica na disponibilização de funcionalidades sem complicar o código.

Ainda na próxima semana teremos a terceira parte da série sobre Blazor, onde vamos finalizar o trabalho de “back-end” e começaremos a colocar as mãos na massa, no Blazor e Web Assembly.

Um abraço a todos e até a próxima!