Проблема:

Предположим, что мы создаем простой дизайн, чтобы получить цены на разные пиццы в пиццерии. Здесь у нас есть несколько пицц, чтобы учитывать различные типы и перекрывающиеся начинки. Как мы можем реализовать дизайн?

Один из способов сделать это — использовать традиционное наследование. Мы создадим базовый класс Pizza и несколько подклассов для различных комбинаций Pizza со ссылкой на начинку в нем.

Теперь давайте найдем, какие здесь глюки:

  1. Для каждого подкласса пиццы должны быть ссылки на начинку. например, в пицце на тонком тесте могут быть помидоры и кукуруза ИЛИ могут быть все 3.
  2. Если мы хотим добавить новые начинки к конкретной пицце, нам нужно изменить код конкретного класса и снова перекомпилировать его.
  3. Каждый подкласс должен иметь логику расчета Cost() по отношению к стоимости каждой начинки.

Из приведенных выше пунктов ясно, что наследование невозможно, поскольку оно статично и применяется ко всему классу. Мы используем наследование или композицию для расширения поведения объекта, но это делается во время компиляции и его применения ко всем экземплярам класса. Мы не можем добавить какую-либо новую функциональность, чтобы удалить какое-либо существующее поведение во время выполнения — в этом случае на сцену выходит шаблон Decorator.

Что такое декоратор?

Шаблоны декораторов просто позволяют вам динамически добавлять функциональные возможности к конкретному объекту, а не к целому классу объектов.

Декораторы предоставляют гибкую альтернативу подклассам для расширения функциональности.

Этот шаблон создает класс декоратора, который обертывает исходный класс и предоставляет дополнительные функциональные возможности, сохраняя сигнатуру методов класса нетронутой.

Не волнуйтесь, давайте разберемся с кодом. Просто помните об этой модели данных.

Сегменты кода написаны в Apex.

Класс пиццы:

public abstract class Pizza{
	String description = 'Pizza ';
	//pizza cost
	public abstract double cost();

	//Pizza Description
	public virtual String description(){
		return description;
	}
}

Пицца на тонком тесте:

public class ThinCrustPizza extends Pizza{
public override String description(){
  return 'Thin Crust';
 }
 
 public override double cost(){
  return 200.0;
 }
}

Пицца из плоского хлеба:

public class FlatBreadPizza extends Pizza{
public override String description(){
  return 'Flat Bread';
 }
 
 public override double cost(){
  return 200.0;
 }
}

Теперь, вплоть до здесь, все круто. Мы приготовили 2 пиццы. Здесь мы преодолели 2 главные проблемы:

  1. У каждого подкласса нет ссылки на начинку.
  2. Поскольку ссылки нет, каждая пицца не зависит от добавления или удаления начинки.

Но они неполные без начинки.

В Inheritance мы используем композицию для добавления начинки, но здесь мы создаем класс декоратора, который обертывает исходный класс и изменяет его поведение во время выполнения.

например, если я хочу добавить кукурузу в свою пиццу, это будет выглядеть примерно так, как показано на рисунке ниже.

Довольно запутанно???.

Ладно, давайте завершим всю структуру, и вы все поймете.

Класс Topping (класс Decorator)

public abstract class Topping extends Pizza{
  public override abstract String description();
}

Лук:

public class Onion extends Topping{
Pizza pizza;
public Onion(Pizza pizza){
  this.pizza = pizza;
 }
 
 public override String description(){
  return this.pizza.description() + ', Onion';
 }
public override double cost(){
  return this.pizza.cost() + 15.0;
 }
}

Сыр:

public class Cheese extends Topping{
Pizza pizza;
public Cheese(Pizza pizza){
  this.pizza = pizza;
 }
 
 public override String description(){
  return this.pizza.description() + ', Cheese';
 }
public override double cost(){
  return this.pizza.cost() + 20.0;
 }
}

Кукуруза:

public class Corn extends Topping{
Pizza pizza;
public Corn(Pizza pizza){
  this.pizza = pizza;
 }
 
 public override String description(){
  return this.pizza.description() + ', Corn';
 }
public override double cost(){
  return this.pizza.cost() + 30.0;
 }
}

наша модель данных меняется на что-то, как показано ниже

Итак, код сделан, модель данных также изменена, но я все равно не могу этого понять.

Давайте выполним, будет понятнее.

Я заказал одну пиццу.

Я хочу добавить кукурузу.

Я тоже хочу добавить сыр.

Итак, наша третья проблема также решена. Поскольку функция стоимости рассчитывается динамически, мы можем добавлять новые начинки, не изменяя логику кода класса пиццы.

Все еще запутался, давайте выполним это программно:

Класс заказа пиццы:

public class OrderPizza{
    
    public OrderPizza(String pizzaName){
        this(pizzaName,false,false,false);
    } 
    
 public OrderPizza(String pizzaName, Boolean addCheese, Boolean addCorn, Boolean addOnion){
        Pizza pizza = null;
        if(pizzaName == 'Thin Crust'){
            pizza = new ThinCrustPizza();
        }
        if(pizzaName == 'Flat Bread'){
             pizza = new FlatBreadPizza();
           
        }
        if(pizza != null){
              displayItems(pizza);
                if(addCheese){
                    pizza = new Cheese(pizza);
                    displayItems(pizza);
                }
                
                if(addCorn){
                    pizza = new Corn(pizza);
                      displayItems(pizza);
                }
              
                if(addOnion){
                    pizza = new Onion(pizza);
                    displayItems(pizza);
                }
                
            }
        
 }
    
    void displayItems(Pizza pizza){
         System.debug('Added '+pizza.description()+ ', Total is Rs. '+pizza.cost());
    }
}

Ввод: (для Java вызовите его в Main)

новый OrderPizza('Плоский хлеб',false,true,true);

Вывод :

Источник: