Leitores como você ajudam a apoiar o MUO. Quando você faz uma compra usando links em nosso site, podemos ganhar uma comissão de afiliado. Consulte Mais informação.

Um dos princípios mais importantes no desenvolvimento de software é o princípio de design aberto-fechado. Esse princípio de design enfatiza que as classes devem ser abertas para extensão, mas fechadas para modificação. O padrão de projeto decorador incorpora o princípio de projeto aberto-fechado.

Com o padrão de projeto decorador, você pode facilmente estender uma classe dando a ela um novo comportamento sem alterar seu código existente. O padrão decorator faz isso dinamicamente em tempo de execução, usando composição. Esse padrão de design é conhecido como uma alternativa flexível ao uso de herança para estender o comportamento.

Como funciona o padrão de design Decorator?

Embora o padrão decorator seja uma alternativa para herança de classe, ele incorpora alguns aspectos de herança em seu design. Um aspecto chave do padrão decorador é que todas as suas classes estão relacionadas, direta ou indiretamente.

instagram viewer

Um típico padrão de projeto decorador tem a seguinte estrutura:

No diagrama de classes acima, você pode ver que o padrão decorator tem quatro classes principais.

Componente: esta é uma classe abstrata (ou interface), que serve como supertipo para o padrão decorator.

Componente de concreto: esses são os objetos que você pode decorar com diferentes comportamentos em tempo de execução. Eles herdam da interface do componente e implementam suas funções abstratas.

Decorador: esta classe é abstrata e tem o mesmo supertipo do objeto que irá decorar. No diagrama de classes, você verá dois relacionamentos entre as classes de componente e decorador. A primeira relação é de herança; cada decorador é um componente. A segunda relação é de composição; cada decorador tem um (ou envolve um) componente.

Decorador de Concreto: esses são os decoradores individuais que dão a um componente um comportamento específico. Você deve observar que cada decorador de concreto possui uma variável de instância que contém uma referência a um componente.

Implementando o Padrão de Design Decorator em Java

Um exemplo de aplicativo de pedido de pizza pode demonstrar adequadamente como usar o padrão decorador para desenvolver aplicativos. Este aplicativo de amostra de pizza permite que os clientes peçam pizzas com vários recheios. A primeira classe do padrão decorator é a interface pizza:

públicointerfacepizza{
públicoabstrato Corda descrição();
públicoabstratodobrocusto();
}

A interface Pizza é a classe do componente. Assim, você pode criar uma ou mais classes concretas a partir dele. A pizzaria faz dois tipos principais de pizzas, com base na massa. Um tipo de pizza tem massa de fermento:

públicoaulaLeveduraCrustPizzaimplementospizza{
@Sobrepor
público Corda descrição(){
retornar"Massa de pizza feita com fermento";
}

@Sobrepor
públicodobrocusto(){
retornar18.00;
}
}

O YeastCrustPizza é o primeiro concreto classe Java da interface Pizza. O outro tipo de pizza disponível é o pão achatado:

públicoaulaFlatbreadCrustPizzaimplementospizza{
@Sobrepor
público Corda descrição(){
retornar"Massa de pizza feita com pão sírio";
}

@Sobrepor
públicodobrocusto(){
retornar15.00;
}
}

A classe FlatbreadCrustPizza é o segundo componente concreto e, como a classe YeastCrustPizza, implementa todas as funções abstratas da interface Pizza.

Os decoradores

A classe decorator é sempre abstrata, então você não pode criar uma nova instância diretamente dela. Mas é preciso estabelecer uma relação entre os diferentes decoradores e os componentes que irão decorar.

públicoabstratoaulaToppingDecoratorimplementospizza{
público Corda descrição(){
retornar"Cobertura Desconhecida";
}
}

A classe ToppingDecorator representa a classe do decorador neste aplicativo de exemplo. Agora a pizzaria pode criar vários recheios (ou decoradores) diferentes, usando a classe ToppingDecorator. Digamos que uma pizza pode ter três tipos diferentes de coberturas, ou seja, queijo, calabresa e cogumelo.

Cobertura De Queijo

públicoaulaQueijoestendeToppingDecorator{
privado Pizza de pizza;

públicoQueijo(Pizza de pizza){
esse.pizza = pizza;
}

@Sobrepor
público Corda descrição(){
retornar pizza.descrição() + ", Recheio De Queijo";
}

@Sobrepor
públicodobrocusto(){
retornarpizza.custo() + 2.50;
}
}

Cobertura De Pepperoni

públicoaulacalabresaestendeToppingDecorator{
privado Pizza de pizza;

públicocalabresa(Pizza de pizza){
esse.pizza = pizza;
}

@Sobrepor
público Corda descrição(){
retornar pizza.descrição() + ", Cobertura De Pepperoni";
}

@Sobrepor
públicodobrocusto(){
retornarpizza.custo() + 3.50;
}
}

Cobertura De Cogumelos

públicoaulaCogumeloestendeToppingDecorator{
privado Pizza de pizza;

públicoCogumelo(Pizza de pizza){
esse.pizza = pizza;
}

@Sobrepor
público Corda descrição(){
retornar pizza.descrição() + ", Cobertura De Cogumelos";
}

@Sobrepor
públicodobrocusto(){
retornarpizza.custo() + 4.50;
}
}

Agora você tem um aplicativo simples implementado usando o padrão de design decorator. Se um cliente pedir uma pizza com massa de fermento com queijo e calabresa, o código de teste para esse cenário será o seguinte:

públicoaulaPrincipal{
públicoestáticovazioprincipal(String[] argumentos){
Pizza pizza1 = novo YeastCrustPizza();
pizza1 = novo Calabresa (pizza1);
pizza1 = novo Queijo (pizza1);
System.out.println (pizza1.description() + " $" + pizza1.custo());
}
}

A execução desse código produzirá a seguinte saída no console:

Como você pode ver, a saída indica o tipo de pizza junto com seu custo total. A pizza começou como uma pizza com massa de fermento por $ 18,00, mas com o padrão decorador, o aplicativo foi capaz de adicionar novos recursos e seu custo adequado à pizza. Assim, dando à pizza um novo comportamento sem alterar o código existente (a pizza com massa de fermento).

Com o padrão decorador, você também pode aplicar o mesmo comportamento a um objeto quantas vezes quiser. Se um cliente pedir uma pizza com tudo e um pouco de queijo extra, você pode atualizar a classe principal com o seguinte código para refletir isso:

Pizza pizza2 = novo YeastCrustPizza();
pizza2 = novo Calabresa (pizza2);
pizza2 = novo Queijo (pizza2);
pizza2 = novo Queijo (pizza2);
pizza2 = novo Cogumelo (pizza2);

System.out.println (pizza2.description() + " $" + pizza2.custo());

O aplicativo atualizado produzirá a seguinte saída no console:

As vantagens de usar o padrão de projeto Decorator

As duas principais vantagens de usar o padrão de projeto decorador são segurança e flexibilidade. O padrão decorador permite que você desenvolva um código mais seguro, não interferindo no código seguro pré-existente. Em vez disso, estende o código existente por meio da composição. Evitando efetivamente a introdução de novos bugs ou efeitos colaterais não intencionais.

Devido à composição, um desenvolvedor também tem muita flexibilidade ao usar o padrão decorator. Você pode implementar um novo decorador a qualquer momento para adicionar um novo comportamento, sem alterar o código existente e interromper o aplicativo.