JavaRush /Java-Blog /Random-DE /Pizzeria-Probleme. Baumeister vs. Dekorateur.
CynepHy6
Level 34
Великий Новгород

Pizzeria-Probleme. Baumeister vs. Dekorateur.

Veröffentlicht in der Gruppe Random-DE

Beschreibung des Problems.

Wir müssen ein Programm schreiben. Für eine Pizzeria, die verschiedene Pizzasorten zubereiten möchte: Hühnchen, Amerikanisch, Fleisch, Hawaiianisch, Peperoni usw. Mal sehen, welche Vorlage und unter welchem ​​Szenario sich zur Lösung dieses Problems eignet. Traditionell wird die Builder-Vorlage zur Lösung von „Pizzaproblemen“ verwendet. Es gibt auch Beispiele für die Verwendung des Decorator-Musters. Beide Muster sind korrekt, es gibt jedoch Unterschiede in der Verwendung. Der Builder ist eine Vorlage zum Erstellen von Objekten, während der Decorator zum spontanen Ändern des fertigen Objekts verwendet wird. Versuchen wir dies anhand von Beispielen zu verstehen:

1. Vorlagenersteller:

In diesem Fall wird die Pizza mit allen Zutaten auf einmal zubereitet.
Pizzakurs:
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 Enum-Klassen:
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(); }
PizzaBuilder-Klasse:
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(); } }
Klassentest:
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. Dekorationsvorlage:

Das Decorator-Muster wird verwendet, um einem Objekt dynamisch zusätzliche Funktionen hinzuzufügen oder zu entfernen, ohne das ursprüngliche Objekt zu beeinträchtigen. Wird verwendet, wenn zuerst ein Pizzaboden zubereitet und dann verschiedene Zutaten hinzugefügt werden. Hier benötigen wir eine Schnittstelle (Pizza) für BasePizza (die grundlegende Komponente), die wir dekorieren möchten, und eine PizzaDecorator-Klasse, die die Schnittstelle tatsächlich implementiert.
Pizza-Schnittstelle:
public interface Pizza { public String bakePizza(); public float getCost(); }
Implementierung in BasePizza:
public class BasePizza implements Pizza{ public String bakePizza() { return "Basic Pizza"; } public float getCost(){ return 100; } }
PizzaDecorator-Klasse:
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 Dekorateure: Pilze und Peperoni
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; } }
Klassentest:
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); } }

Unterschiede

Muster wie Builder und Factory (und Abstract Factory) werden bei der Erstellung neuer Objekte verwendet. Und Muster wie Decorator (auch bekannt als Structural Design Patterns) werden zur Erweiterbarkeit oder zur Bereitstellung struktureller Änderungen an bereits erstellten Objekten verwendet. Beide Arten von Mustern fördern in erster Linie die Komposition durch Vererbung, und die Unterschiede sind nicht signifikant genug, um die Verwendung eines Builders anstelle eines Decorators zu rechtfertigen. Beide geben bei der Ausführung ihr eigenes Verhalten an, anstatt es zu erben. In einem Fall ist es besser, den Builder zu verwenden – wenn wir die Erstellung von Objekten mit bestimmten Eigenschaften/Funktionen einschränken möchten. Es gibt beispielsweise 4-5 Attribute, die vor der Erstellung eines Objekts festgelegt werden müssen, oder wir möchten die Erstellung eines Objekts einfrieren, bis bestimmte Attribute bereits festgelegt sind. Einfach ausgedrückt: Verwenden Sie es anstelle eines Konstruktors – wie Joshua Bloch in Java: Effective Programming, 2. Auflage, schreibt. Der Builder stellt die Attribute bereit, die das generierte Objekt haben sollte, verbirgt jedoch, wie diese festgelegt werden. Ein Dekorator wird verwendet, um einem vorhandenen Objekt neue Eigenschaften hinzuzufügen, wenn darauf basierend ein neues Objekt erstellt wird. Es gibt keine Einschränkungen beim Einfrieren eines Objekts, während alle seine Funktionen hinzugefügt werden. Beide Vorlagen verwenden Komposition und sehen möglicherweise ähnlich aus. Der Hauptunterschied liegt in ihrer Verwendung.
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION