JavaRush /Blogue Java /Random-PT /Problemas na pizzaria. Construtor vs Decorador.
CynepHy6
Nível 34
Великий Новгород

Problemas na pizzaria. Construtor vs Decorador.

Publicado no grupo Random-PT

Descrição do problema.

Precisamos escrever um programa. Para uma pizzaria que deseja preparar diferentes tipos de pizza: Frango, Americana, Carne, Havaiana, Calabresa, etc. Vamos ver qual modelo e em qual cenário é adequado para resolver este problema. Tradicionalmente, o modelo Builder é usado para resolver “problemas de pizza”. Existem também exemplos de uso do padrão Decorator, ambos os padrões estão corretos, mas existem diferenças no uso. O Builder é um modelo para criar objetos, enquanto o Decorator é usado para modificar o objeto finalizado em tempo real. Vamos tentar entender isso com exemplos:

1. Construtor de modelo:

Neste caso, a pizza é preparada com todos os ingredientes de uma só vez.
Aula de pizza:
public class Pizza{ private float totalPrice = 0; private Size size; private Topping topping; private Crust crust; private Cheese cheese; public Size getSize(){ return size; } public void setSize(Size size){ this.size = size; } public Topping getTopping(){ return topping; } public void setTopping(Topping topping){ this.topping = topping; } public Crust getCrust(){ return crust; } public void setCrust(Crust crust){ this.crust = crust; } public Cheese getCheese(){ return cheese; } public void setCheese(Cheese cheese){ this.cheese = cheese; } public float getTotalPrice(){ return totalPrice; } public void addToPrice(float price){ this.totalPrice = totalPrice + price; } }
4 classes de enumeração:
public enum Cheese { AMERICAN{ public float getCost(){ return 40; } }, ITALIAN { public float getCost(){ return 60; } }; public abstract float getCost(); } public enum Crust { THIN{ public float getCost(){ return 70; } } , STUFFED{ public float getCost(){ return 90; } }; public abstract float getCost(); } public enum Size { MEDIUM { public float getCost() { return 100; } }, LARGE { public float getCost() { return 160; } }; public abstract float getCost(); } public enum Topping { PEPPERONI { public float getCost(){ return 30; } }, CHICKEN{ public float getCost(){ return 35; } }, MUSHROOM{ public float getCost(){ return 20; } }; public abstract float getCost(); }
Classe PizzaBuilder:
public class PizzaBuilder { Pizza pizza = new Pizza(); public PizzaBuilder withTopping(Topping topping) { pizza.setTopping(topping); pizza.addToPrice(topping.getCost()); return this; } public PizzaBuilder withSize(Size size) { pizza.setSize(size); pizza.addToPrice(size.getCost()); return this; } public PizzaBuilder withCrust(Crust crust) { pizza.setCrust(crust); pizza.addToPrice(crust.getCost()); return this; } public Pizza build() { return pizza; } public double calculatePrice() { return pizza.getTotalPrice(); } }
Teste de aula:
public class PizzaBuilderTest { @Test public void shouldBuildThinCrustChickenPizza(){ Pizza pizza = new PizzaBuilder().withCrust(Crust.THIN).withTopping(Topping.CHICKEN).withSize(Size.LARGE).build(); assertEquals(Topping.CHICKEN,pizza.getTopping()); assertEquals(Size.LARGE,pizza.getSize()); assertEquals(Crust.THIN,pizza.getCrust()); assertEquals(265.0,pizza.getTotalPrice(),0); } }

2. Modelo de decorador:

O padrão Decorator é usado para adicionar ou remover funcionalidades adicionais de um objeto dinamicamente, sem afetar o objeto original. Usado quando uma base de pizza é preparada primeiro e depois vários ingredientes são adicionados. Aqui precisamos de uma interface (Pizza) para BasePizza (o componente fundamental) que queremos decorar e uma classe PizzaDecorator que realmente implemente a interface.
Interface de pizza:
public interface Pizza { public String bakePizza(); public float getCost(); }
Implementação em BasePizza:
public class BasePizza implements Pizza{ public String bakePizza() { return "Basic Pizza"; } public float getCost(){ return 100; } }
Classe PizzaDecorator:
public class PizzaDecorator implements Pizza { Pizza pizza; public PizzaDecorator(Pizza newPizza) { this.pizza = newPizza; } public String bakePizza() { return pizza.bakePizza(); } @Override public float getCost() { return pizza.getCost(); } }
2 decoradores: Cogumelo e Calabresa
public class Mushroom extends PizzaDecorator { public Mushroom(Pizza newPizza) { super(newPizza); } @Override public String bakePizza() { return super.bakePizza() + " with Mushroom Topings"; } @Override public float getCost() { return super.getCost()+80; } } public class Pepperoni extends PizzaDecorator { public Pepperoni(Pizza newPizza) { super(newPizza); } @Override public String bakePizza() { return super.bakePizza() + " with Pepperoni Toppings"; } @Override public float getCost() { return super.getCost()+110; } }
Teste de aula:
public class PizzaDecoratorTest { @Test public void shouldMakePepperoniPizza(){ Pizza pizza = new Pepperoni(new BasePizza()); assertEquals("Basic Pizza with Pepperoni Toppings",pizza.bakePizza()); assertEquals(210.0,pizza.getCost(),0); } }

Diferenças

Padrões como Builder e Factory (e Abstract Factory) são usados ​​na criação de novos objetos. E padrões como Decorator (também conhecidos como Structural Design Patterns) são usados ​​para extensibilidade ou para fornecer alterações estruturais a objetos já criados. Ambos os tipos de padrões promovem principalmente a composição por meio de herança, e as diferenças não são significativas o suficiente para justificar o uso de um Construtor em vez de um Decorador. Ambos apresentam seu próprio comportamento quando executados, em vez de herdá-lo. Num caso é melhor usar o Builder - se quisermos limitar a criação de objetos com certas propriedades/funções. Por exemplo, existem 4-5 atributos que devem ser definidos antes de criar um objeto, ou queremos congelar a criação de um objeto até que certos atributos ainda sejam definidos. Simplificando, use-o em vez de um construtor - como Joshua Bloch escreve em Java: Effective Programming, 2nd ed. O construtor fornece os atributos que o objeto gerado deve ter, mas oculta como defini-los. Um decorador é usado para adicionar novas propriedades a um objeto existente ao criar um novo objeto baseado nele. Não há restrições para congelar um objeto enquanto todos os seus recursos estão sendo adicionados. Ambos os modelos usam composição e podem ser semelhantes. A principal diferença está no seu uso.
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION