JavaRush /Java блог /Архив info.javarush /Проблемы пиццерии. Строитель против Декоратора.
CynepHy6
34 уровень
Великий Новгород

Проблемы пиццерии. Строитель против Декоратора.

Статья из группы Архив info.javarush

Описание проблемы.

Нам необходимо написать программу. Для пиццерии, которая хочет готовить различные виды пиццы Куриная, Американка, Мясная, Гавайская, Пепперони и тд. Давайте посмотрим какой шаблон и под каким сценарием подойдет для решения этой проблемы. Традиционно для решения “пицца-проблем” используют шаблон Строитель. А также есть примеры использования шаблона Декоратор, оба шаблона корректны, но есть различия в использовании. Строитель — это шаблон создания объектов, в то время как Декоратор используется для изменения готового объекта на лету. Попробуем разобраться в этом на примерах:

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-я ред. Строитель предоставляет атрибуты которые сгенерированный объект должен иметь но скрывает как установить их. Декоратор используется для добавления новых свойств существующему объекту при создании нового объекта на его основе. Тут нет ограничений по замораживанию объекта пока все его особенности добавляются. Оба шаблона используют композицию и они могут выглядеть похоже. В основном разница в их использовании.
Комментарии (3)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
silh Уровень 0
14 октября 2014
Здесь нам нужен интерфейс (Pizza) для BasePizza (фундаментальный компонент) который мы хотим декорировать и класс PizzaDecorator который реализует интерфе йс Pizza (декорирует).
silh Уровень 0
14 октября 2014
Шаблон Декоратор используется для добавления или удаления дополнительной функциональности в объекте динамически, не влияя на исходный объект. Используется в случае, когда некоторая база для пиццы готовится в первцю очередь а затем добавляются различные ингридиенты.
Немного подправить.