JavaRush /Blog Java /Random-ES /Problemas de pizzería. Constructor vs Decorador.
CynepHy6
Nivel 34
Великий Новгород

Problemas de pizzería. Constructor vs Decorador.

Publicado en el grupo Random-ES

Descripción del problema.

Necesitamos escribir un programa. Para una pizzería que quiera preparar diferentes tipos de pizza: Pollo, Americana, Carne, Hawaiana, Pepperoni, etc. Veamos qué plantilla y bajo qué escenario es adecuada para solucionar este problema. Tradicionalmente, la plantilla Builder se utiliza para resolver "problemas de pizza". También hay ejemplos del uso del patrón Decorador, ambos patrones son correctos, pero existen diferencias en el uso. El Constructor es una plantilla para crear objetos, mientras que el Decorador se utiliza para modificar el objeto terminado sobre la marcha. Intentemos entender esto con ejemplos:

1. Creador de plantillas:

En este caso la pizza se prepara con todos los ingredientes a la vez.
Clase 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 clases de enumeración:
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(); }
Clase 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(); } }
Prueba de clase:
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. Plantilla de decorador:

El patrón Decorador se utiliza para agregar o eliminar funcionalidad adicional a un objeto de forma dinámica sin afectar el objeto original. Se utiliza cuando primero se prepara alguna base de pizza y luego se añaden varios ingredientes. Aquí necesitamos una interfaz (Pizza) para BasePizza (el componente fundamental) que queremos decorar y una clase PizzaDecorator que realmente implemente la interfaz.
Interfaz de pizza:
public interface Pizza { public String bakePizza(); public float getCost(); }
Implementación en BasePizza:
public class BasePizza implements Pizza{ public String bakePizza() { return "Basic Pizza"; } public float getCost(){ return 100; } }
Clase 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: Champiñones y 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; } }
Prueba de clase:
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); } }

Diferencias

Patrones como Builder y Factory (y Abstract Factory) se utilizan en la creación de nuevos objetos. Y patrones como Decorator (también conocido como patrones de diseño estructural) se utilizan para lograr extensibilidad o para proporcionar cambios estructurales a objetos ya creados. Ambos tipos de patrones promueven principalmente la composición a través de la herencia, y las diferencias no son lo suficientemente significativas como para justificar el uso de un Constructor en lugar de un Decorador. Ambos dan su propio comportamiento cuando se ejecutan, en lugar de heredarlo. En un caso, es mejor usar el Constructor, si queremos limitar la creación de objetos con ciertas propiedades/funciones. Por ejemplo, hay 4 o 5 atributos que deben configurarse antes de crear un objeto, o queremos congelar la creación de un objeto hasta que aún se establezcan ciertos atributos. En pocas palabras, utilícelo en lugar de un constructor, como escribe Joshua Bloch en Java: Programación efectiva, 2ª ed. El constructor proporciona los atributos que debería tener el objeto generado, pero oculta cómo configurarlos. Un decorador se utiliza para agregar nuevas propiedades a un objeto existente al crear un nuevo objeto basado en él. No existen restricciones para congelar un objeto mientras se agregan todas sus funciones. Ambas plantillas usan composición y pueden parecer similares. La principal diferencia está en su uso.
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION