Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
O Decorator é um padrão do tipo estrutural, sua principal finalidade é adicionar novos comportamentos a objetos já existentes, de forma dinâmica e que deixe a implementação mais flexível, tudo isso sem alterar a estrutura original.
Esse tipo de design pattern é perfeito para implementações incrementais, sua implementação é simplesmente criar decoradores que envolvem classes e adicionam funcionalidades.
Para implementar esse pattern vamos precisar entender alguns conceitos, e papéis para que você consiga encaixar esse padrão na sua lógica de negócio, a seguir vamos exemplificar cada um dos papéis e mostrar uma implementação de fato.
Para que seja possível a implementação vamos precisar de um componente ou component, essa interface que será o responsável por definir o comportamento de nossas classes, aqui deverão constar as assinaturas que definirão o contrato que nossos objetos deverão seguir, vamos ao exemplo:
public interface Cafe {
Double getValor();
String getDescricao();
}
Cafe.javaEssa é de fato a implementação que seguirá o contrato pré estabelecido do nosso componente, essa parte é bem simples basta implementar a interface do componente e cada um de seus métodos obrigatórios.
public class CafeSimples implements Cafe {
@Override
public Double getValor() {
return 10.0;
}
@Override
public String getDescricao() {
return "Café simples";
}
}
CafeSimples.javaAgora vamos à implementação que de fato dá nome ao pattern, essa se trata de criar uma interface capaz de encapsular o nosso objeto concreto e que implementa nosso componente, aqui também vamos fazer o uso dos métodos do componente concreto encapsulando-os com nossos próprios métodos.
public abstract class CafeDecorator implements Cafe {
protected Cafe decoretedCafe;
public CafeDecorator(Cafe decoretedCafe) {
super();
this.decoretedCafe = decoretedCafe;
}
@Override
public Double getValor() {
return decoretedCafe.getValor();
}
@Override
public String getDescricao() {
return decoretedCafe.getDescricao();
}
}
CafeDecorator.javaE aqui cabe uma observação, toda vez que falamos em interface e estamos falando de Java, pode-se entender que é obrigatório o uso do recurso interface presente no Java, e não se trata disso, o objetivo aqui é proporcionar um jeito desacoplado de ter um contrato com alguma a implementação já concreta, por isso estamos utilizando uma classe abstrata, que na linguagem Java é o que nos proporciona de uma maneira fácil esse tipo de recurso, lembrando que por ser uma classe abstrata não é possível construir um objeto a partir dela, essa classe só poderá ser estendida, forçando assim uma implementação do decorator.
Essa é de fato a implementação do decorator, onde vamos adicionar novos comportamentos ao que já foi implementado antes. Essa classe tem que obedecer o contrato do Decorator que por sua vez já encapsula as responsabilidades do nosso componente.
public class CafeComLeite extends CafeDecorator {
public CafeComLeite(Cafe decoretedCafe) {
super(decoretedCafe);
}
@Override
public Double getValor() {
return super.getValor() + 0.90;
}
@Override
public String getDescricao() {
return super.getDescricao() + " com leite";
}
}
CafeComLeite.javaAgora vamos testar a nossa implementação, instanciando as nossas classes e observando o comportamento.
public class ExemploDecorator {
public static void main(String[] args) {
CafeSimples cafe = new CafeSimples();
System.out.println(String.format("Preço: R$ %.2f, Descrição: %s", cafe.getValor(), cafe.getDescricao()));
CafeComLeite cafeComLeite = new CafeComLeite(cafe);
System.out.println(String.format("Preço: R$ %.2f, Descrição: %s", cafeComLeite.getValor(), cafeComLeite.getDescricao()));
}
}
ExemploDecorator.javaAnalisando agora a implementação de fato, note que após definir um Decorator, podemos definir inúmeros decoradores concretos, com base no nosso decorator, é isso que torna esse pattern dinâmico e flexível, pois cada implementação nova, conseguimos adicionar novas funcionalidades, a uma implementação já existente, sem ferir contrato ou atrapalhando alguma funcionalidades do nosso sistema.
Como todo design pattern existem prós e contras no seu uso, a flexibilidade e a implementação incremental sem dúvidas são muito bons para o desenvolvimento no dia a dia, mas por outro lado existem contras importantes a serem observados no uso desse padrão.
Importante observar que a alteração no componente decorado afeta o seu decorador, já que o mesmo utiliza seus métodos e acrescenta funcionalidades, por isso é importante observar se na regra de negócio que você está trabalhando esse tipo de acoplamento cabe.
O uso desse padrão quando em maior escala pode adicionar uma complexidade a mais, quanto mais e mais decorados você implementar, mais complexo pode ficar o seu código, e mais difícil de se entender.
Outro ponto a se observar é que o nível de hierarquia dos decoradores pode ser um ponto problemático se você possui um empilhamento de decoradores, pode ser problemático manter o comportamento adequado de alguns objetos.
Se você empilhar demais os decoradores, pode acabar com uma quantidade muito excessiva de instâncias de objetos, importante observar se isso não pode comprometer o desempenho das suas aplicações assim como o uso excessivo de recursos como a memória.
Por último mas não menos importante é importante lembrar, que o uso excessivo pode complicar quando houver a necessidade de remover algum componente, pois de certa forma esses estão acoplados uns aos outros, e a remoção de um pode comprometer a funcionalidade de outro.
O uso desse padrão é de fato bem interessante, principalmente quando já temos implementações e precisamos incrementar adicionando novos comportamentos. Em um sistema grande pode acabar sendo a melhor escolha, para implementações incrementais, a depender do modo como a implementação foi organizada. Vale ressaltar alguns problemas que esse padrão pode nos trazer, como um acoplamento não desejado, ou uma complexidade excessiva, mas de fato é mais um conceito muito bom e se utilizado da melhor forma, torna-se muito poderoso nas mãos de qualquer desenvolvedor.