Você já ouviu falar desses padrões de projeto? Sabe a diferença entre eles? A verdade é que esses dois padrões de projeto possuem usos bem distintos, mas sua implementação é relativamente semelhante, uma vez que ambos utilizam como base a **composição de classes**. Assim, muitas vezes ficamos com uma pulga atrás da orelha sobre como e quando aplicar cada um desses padrões. Hoje, eu vou tentar **descomplicar a aplicabilidade** e **conceito** desses dois padrões estruturais, trazidos pela **GoF** (Gang of Four) em seu livro "**Padrões de Projeto**". [TOC] ## 1. Padrões de Projeto Estruturais De acordo com a **GoF**: "*Os padrões estruturais se preocupam com a forma como classes e objetos são compostos para formar estruturas maiores*". Em suma, diferente dos **padrões criacionais** que abstraem processos de instanciação, e dos **padrões comportamentais** que se preocupam mais com o algorítmo e a atribuição de responsabilidades entre objetos, os **padrões estruturais** se preocupam com a "forma" das classes, reaproveitando a herança de objetos simples para composição de estruturas mais complexas. ## 2. O Composite ### 2.1. Conceito O Composite é o **quarto padrão** desta categoria apresentado pela GoF, e o primeiro que eu pretendo trazer neste artigo. Como os próprios autores colocam, a intenção oficial deste padrão seria "*compor objetos em estruturas de árvore para representarem hierarquias partes-todo. Composite permite aos clientes tratarem de maneira uniforme objetos individuais e composições de objetos*". Ficou confuso? Não se preocupe que a ideia aqui é justamente destrinchar essa intenção e entender na prática o que isso significa. ![](https://i.imgur.com/spexC5b.png) *Diagrama oficial do livro "Padrões de Projetos: Soluções Reutilizáveis de Software Orientados a Objetos"* A ideia por trás deste padrão, nada mais é do que a invocação encadeada de um método, delegada pela classe **Composite**, assim chamando a **implementação de seus filhos**. ### 2.2. Exemplo prático Vejamos o seguinte cenário: Em um supermercado, você possui duas formas de comprar um refrigerante; você pode comprar a **unidade**, ou comprar um **fardo com 12 unidades**. Aqui, nós temos um **Composite**: a noção de que o **fardo** (*estrutura*) se comporta como um produto, e que ele pode dizer seu preço delegando o seu valor, somando assim os custos das unidades filhas e utilizando como seu próprio. Claro, o cenário acima só faz sentido se o fardo não possuir um valor fixo, e sim que seu valor se resume no total de unidades que há dentro do mesmo. Vejamos um código do cenário descrito (no caso, utilizando Java para exemplificar): ```Java= public interface Produto { public Double getValor(); } ``` No exemplo acima, definimos uma interface **Produto**, que será implementada tanto pela unidade de "refrigerante", quanto pelo fardo. O método em comum que será implementado por eles, no nosso caso, é o `getValor()`. --- ```Java= public class Refrigerante implements Produto { private String nome; private Double valor; public Refrigerante(String nome, Double valor) { this.nome = nome; this.valor = valor; } public String getNome() { return nome; } @Override public Double getValor() { return valor; } } ``` O refrigerante, por sua vez, possui o valor e um nome. A implementação do `getValor()`, por ser uma única unidade, apenas retorna o seu próprio valor. ```Java= public class FardoDeRefrigerante implements Produto { private List<Produto> produtos = new ArrayList<>(); public void add(Produto ...produtos) { this.produtos.addAll(Arrays.asList(produtos)); } public void add(Produto produto, int quantidade) { for (int i = 0; i < quantidade; i++) { this.produtos.add(produto); } } public void remove(Produto produto) { produtos.remove(produto); } @Override public Double getValor() { Double soma = 0d; for (Produto produto : produtos) { soma += produto.getValor(); } return soma; } } ``` Acima temos o fardo de refrigerantes, atuando como **Composite**, seu "valor" é delegado para as unidades que há dentro do mesmo. Ou seja, se houver 5 unidades de refrigerante, cada um com um valor "5", o valor do fardo será de 25. ```Java= Produto soda = new Refrigerante("Soda", 3.5); FardoDeRefrigerante fardoDeRefrigerante = new FardoDeRefrigerante(); fardoDeRefrigerante.add(soda, 6); ``` ```Java= Produto soda1 = new Refrigerante("Soda", 2.75); Produto soda2 = new Refrigerante("Soda", 3.99); ... FardoDeRefrigerante fardoDeRefrigerante = new FardoDeRefrigerante(); fardoDeRefrigerante.add(soda1, soda2, ...); ``` O interessante neste padrão de projetos, é que a complexidade desse tipo de estrutura não acaba por aí. Como a classe `FardoDeRefrigerante` possui uma lista dentro de si do tipo `Produto`, ela é capaz de suportar até mesmo outros fardos dentro de si mesma. Apesar de o "fardo de refrigerante" não ser o melhor exemplo nesse caso, podemos imaginar uma caixa dentro de outra caixa, e cada uma delas com produtos de diferentes valores dentro de sí, as caixas internas totalizariam o valor dos produtos internos, e a caixa externa totalizaria o valor das caixas internas, parecendo muito com a estrutura abaixo: ![](https://i.imgur.com/nQ9G8Nn.png) *Exemplo de como a estrutura de um composite pode ficar. Fonte: "Padrões de Projetos: Soluções Reutilizáveis de Software Orientados a Objetos"* ## 3. O Decorator ### 3.1. Conceito O **Decorator**, também comumente chamado de "*Wrapper*", é o **quinto padrão** de projeto estrutural listado pela GoF. Sua intenção é relativamente simples de se entender, uma vez que ela apenas busca acrescentar alguma funcionalidade dentro de classes. Conforme descrito pelos autores: "*[Sua intenção é] dinamicamente, agregar responsabilidades adicionais a um objeto. Os Decorators fornecem uma alternativa flexível ao uso de subclasses para extensão de funcionalidades*". ![](https://i.imgur.com/hCoY6Dt.png) *Diagrama oficial do livro “Padrões de Projetos: Soluções Reutilizáveis de Software Orientados a Objetos”* A semelhança entre o **diagrama do Decorator** e **Composite** não é atoa: como dito no início do artigo, ambos utilizam **composição de classes**. Este é um dos motivos por às vezes haver confusão entre esses dois padrões de projeto. ### 3.2. Exemplo prático Vejamos o seguinte cenário: Em uma determinada lanchonete, cada um dos lanches disponíveis no cadápio possui sua versão normal, gourmet e vegano. Trazendo para o mundo do desenvolvimento, de qual forma poderíamos reaproveitar o máximo de métodos/atributos em comum entre cada um dos lanches (objetos)? Aqui, podemos aplicar o decorator, onde o **lanche gourmet** e **vegano** são "variantes" de um **lanche comum**. Segue exemplo abaixo, utilizando um X-Salada e um Cachorro-quente como opções de cardápio: ```Java= public interface Lanche { public Double getValor(); } ``` Podemos começar com uma declaração simples do que poderia ser o lanche. Para simplificar o Decorator, vamos apenas alterar o preço entre gourmet, vegano e comum. Assim, vamos ficar apenas com o método `getValor()`. --- ```Java= public class XSalada implements Lanche { private Double valor; // ...código omitido public XSalada(Double valor) { this.valor = valor; } @Override public Double getValor() { return valor; } // ...código omitido } ``` ```Java= public class CachorroQuente implements Lanche { private Double valor; // ...código omitido public CachorroQuente(Double valor) { this.valor = valor; } @Override public Double getValor() { return valor; } // ...código omitido } ``` Aqui, temos a implementação dos nossos lanches "comuns". Estes objetos também são chamados de "componentes concretos", dentro deste padrão de projeto. Cada um dos componentes concretos possui seus próprios atributos e métodos. Para simplificar, vamos focar apenas no valor dos lanches. --- ```Java= public abstract class LancheDecorator implements Lanche { protected Lanche lanche; protected LancheDecorator(Lanche lanche) { this.lanche = lanche; } } ``` Então, a primeira implementação do nosso **Decorator** aparece. Neste primeiro momento, ele serve apenas como uma classe intermediária para os **decoradores concretos**, que virão a seguir. Em alguns cenários (inclusive o nosso), a criação deste método torna-se opcional, uma vez que ele apenas define a forma de instanciação dos decoradores filhos. Ainda assim, neste exemplo nós seguiremos a estrutura proposta pela GoF. Em um cenário mais real, esta classe poderia possuir alguns métodos e obrigar a sua implementação para as classes filhas. No nosso caso, estamos apenas delegando a implementação de `getValor()`, que vem da interface `Lanche`. --- Como **decoradores concretos**, conforme nosso exemplo, teríamos os seguintes: ```Java= public class LancheVegano extends LancheDecorator { public LancheVegano(Lanche lanche) { super(lanche); } @Override public Double getValor() { return this.lanche.getValor() + 7.5; } } ``` ```Java= public class LancheGourmet extends LancheDecorator { public LancheGourmet(Lanche lanche) { super(lanche); } @Override public Double getValor() { return this.lanche.getValor() + 9.95; } } ``` Como pode ser observado, os **Decorators**, especificamente no nosso caso, possuem apenas uma modificação de incremento de valor. Esse "comportamento extra" trazido aos lanches, servirá como modelo para decorar componentes concretos, como por exemplo o `XSalada` ou o `CachorroQuente`. Quando um `LancheGourmet` for instanciado com um `XSalada` como parâmetro portanto, um valor de "9,95" será acrescentado ao valor original do x-salada, e quando um `LancheVegano` for instanciado com um `CachorroQuente` como parâmetro portanto, um valor de "7,5" será acrescentado ao valor original do cachorro-quente. Obviamente, o incremento de valor é apenas um exemplo. Utilizando deste conceito, qualquer adição de funcionalidade ou alteração de comportamento poderia ser aplicado nos lanches. O importante é compreender o objetivo: ao invés de criarmos as classes `CachorroQuente`, `CachorroQuenteVegano`, `CachorroQuenteGourmet`, `XSalada`, `XSaladaVegano`, `XSaladaGourmet` etc., basta decorarmos os componentes concretos (`XSalada` e `CachorroQuente`) com nossos decoradores concretos. ```Java= Lanche xSalada = new XSalada(12d); // Valor de 12,00 Lanche xSaladaGoumert = new LancheGourmet(xSalada); // Valor de 21,95 Lanche xSaladaVegano = new LancheVegano(xSalada); // Valor de 19,50 ``` ## 4. Opinião do Autor Acredito que esses dois padrões de projetos são muito interessantes, e de certa forma é bem comum de vê-los em sistemas. O padrão de projeto **Composite** é normalmente utilizado, por exemplo, para rodar uma bateria de validações em cima de um objeto. Enquanto o **Decorator** serve para extender funcionalidades dentro de uma classe já existente, sem alterar a classe original e preservando os princípios **SOLID**. De qualquer forma, espero ter conseguido elucidar os seus usos e descomplicado os seus conceitos. Claro que a forma de implementação pode variar bastante dependendo de como que você utiliza, mas o importante é conhecer o padrão e saber quando encaixá-lo no seu sistema. Não siga à risca os diagramas propostos pela GoF, ou os exemplos citados; adeque-os de acordo com suas necessidades e situações.