JavaRush /Blog Java /Random-PL /Problemy z pizzerią. Konstruktor kontra dekorator.
CynepHy6
Poziom 34
Великий Новгород

Problemy z pizzerią. Konstruktor kontra dekorator.

Opublikowano w grupie Random-PL

Opis problemu.

Musimy napisać program. Dla pizzerii chcącej przygotować różne rodzaje pizzy: Kurczakowa, Amerykańska, Mięsna, Hawajska, Pepperoni itp. Zobaczmy, który szablon i w jakim scenariuszu jest odpowiedni do rozwiązania tego problemu. Tradycyjnie szablon Builder służy do rozwiązywania „problemów z pizzą”. Istnieją również przykłady wykorzystania wzorca Dekorator, oba wzorce są poprawne, jednak istnieją różnice w zastosowaniu. Builder jest szablonem do tworzenia obiektów, zaś Dekorator służy do modyfikowania gotowego obiektu na bieżąco. Spróbujmy to zrozumieć na przykładach:

1. Kreator szablonów:

W tym przypadku pizzę przygotowuje się ze wszystkich składników na raz.
Klasa pizzy:
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 klasy wyliczeniowe:
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(); }
Klasa 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(); } }
Próba klasowa:
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. Szablon dekoratora:

Wzorzec Dekorator służy do dynamicznego dodawania lub usuwania dodatkowych funkcji z obiektu bez wpływu na oryginalny obiekt. Używane, gdy najpierw przygotowuje się bazę do pizzy, a następnie dodaje się różne składniki. Tutaj potrzebujemy interfejsu (Pizza) dla BasePizza (podstawowy komponent), który chcemy udekorować oraz klasy PizzaDecorator, która faktycznie implementuje interfejs.
Interfejs pizzy:
public interface Pizza { public String bakePizza(); public float getCost(); }
Implementacja w BasePizza:
public class BasePizza implements Pizza{ public String bakePizza() { return "Basic Pizza"; } public float getCost(){ return 100; } }
Klasa 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 dekoratory: Pieczarkowy i Pepperoni
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; } }
Próba klasowa:
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); } }

Różnice

Wzory takie jak Konstruktor i Fabryka (oraz Fabryka Abstrakcyjna) są wykorzystywane przy tworzeniu nowych obiektów. Wzorce takie jak Dekorator (znane również jako wzorce projektów strukturalnych) służą do rozszerzania lub wprowadzania zmian strukturalnych w już utworzonych obiektach. Obydwa typy wzorców promują przede wszystkim kompozycję poprzez dziedziczenie, a różnice nie są na tyle znaczące, aby uzasadniać użycie Konstruktora zamiast Dekoratora. Obydwa podają własne zachowanie po wykonaniu, zamiast je dziedziczyć. W jednym przypadku lepiej skorzystać z Buildera - jeśli chcemy ograniczyć tworzenie obiektów o określonych właściwościach/funkcjach. Na przykład istnieje 4-5 atrybutów, które należy ustawić przed utworzeniem obiektu lub chcemy zamrozić tworzenie obiektu do czasu ustawienia pewnych atrybutów. Najprościej mówiąc, użyj go zamiast konstruktora – jak pisze Joshua Bloch w Java: Effective Programming, 2nd ed. Konstruktor zapewnia atrybuty, które powinien mieć wygenerowany obiekt, ale ukrywa sposób ich ustawienia. Dekorator służy do dodawania nowych właściwości do istniejącego obiektu podczas tworzenia na jego podstawie nowego obiektu. Nie ma żadnych ograniczeń dotyczących zamrażania obiektu podczas dodawania wszystkich jego funkcji. Obydwa szablony wykorzystują kompozycję i mogą wyglądać podobnie. Główna różnica polega na ich zastosowaniu.
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION