JavaRush /Java блог /Random UA /Проблеми піцерії. Будівник проти Декоратора.
CynepHy6
34 рівень
Великий Новгород

Проблеми піцерії. Будівник проти Декоратора.

Стаття з групи Random UA

Опис проблеми.

Нам потрібно написати програму. Для піцерії, яка хоче готувати різні види піци Куряча, Американка, М'ясна, Гавайська, Пепероні тощо. Давайте подивимося, який шаблон і під яким сценарієм підійде для вирішення цієї проблеми. Традиційно для вирішення піци-проблем використовують шаблон Будівельник. Також є приклади використання шаблону Декоратор, обидва шаблони коректні, але є відмінності у використанні. Будівельник - це шаблон створення об'єктів, в той час як Декоратор використовується для зміни готового об'єкта на льоту. Спробуємо розібратися в цьому на прикладах:

1. Шаблон Будівельник:

У цьому випадку піца готується одразу з усіма інгредієнтами.
Клас 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 класи-перерахування:
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:
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(); } }
Клас-тест:
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. Шаблон Декоратор:

Шаблон Декоратор використовується для додавання або видалення додаткової функціональності об'єкта динамічно, не впливаючи на вихідний об'єкт. Використовується у випадку, коли деяка база для піци готується в першу чергу, а потім додаються різні інгредієнти. Тут нам потрібен інтерфейс (Pizza) для BasePizza (фундаментальний компонент), який ми хочемо декорувати і клас PizzaDecorator, який власне і реалізує інтерфейс.
Інтерфейс Pizza:
public interface Pizza { public String bakePizza(); public float getCost(); }
Реалізація у BasePizza:
public class BasePizza implements Pizza{ public String bakePizza() { return "Basic Pizza"; } public float getCost(){ return 100; } }
Клас 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 декоратори: Mushroom та 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; } }
Клас-тест:
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); } }

Відмінності

Шаблони, такі як Будівельник та Фабрика (і Абстрактна Фабрика) використовуються для створення нових об'єктів . А шаблони, такі як Декоратор ( також відомі як Структурні шаблони проектування), використовуються для розширюваності або забезпечення структурних змін вже створених об'єктів. Обидва типи шаблонів в основному сприяють композиції через успадкування і відмінності не настільки значні, щоб використовувати Будівельник замість Декоратора. Обидва дають свою поведінку під час виконання, а не успадковують її. В одному випадку краще використовувати Будівельник — якщо ми хочемо обмежити створення об'єктів із певними властивостями/функціями. Наприклад, є 4-5 атрибути, які повинні бути обов'язково встановлені до створення об'єкта або ми хочемо заморозити створення об'єкта доки певні атрибути не ще не встановлені. Простіше кажучи, використовуйте його замість конструктора - як Джошуа Блох пише в "Java. Ефективне програмування", 2-а ред. Будівельник надає атрибути, які згенерований об'єкт повинен мати але приховує як встановити їх. Декоратор використовується для додавання нових властивостей існуючому об'єкту під час створення нового об'єкта з його основі. Тут немає обмежень щодо заморожування об'єкта поки всі його особливості додаються. Обидва шаблони використовують композицію і можуть виглядати схоже. В основному різниця у їх використанні.
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ