Поймите "образец декоратора" с примером реального мира

Я изучал шаблон Decorator, как описано в GOF.

Пожалуйста, помогите мне понять узор декоратора. Может ли кто-нибудь привести пример использования, где это полезно в реальном мире?

Ответ 1

Рисунок декоратора достигает единственной цели динамического добавления ответственности перед любым объектом.

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

Здесь идет узор декоратора.

В соответствии с рисунком декоратора вы будете использовать начинки, поскольку декораторы и пиццы будут украшены декораторами из начинок. Практически каждый клиент хотел бы получить начинки своего желания, а окончательная сумма счета будет состоять из базовой пиццы и дополнительно заказанных начинок. Каждый украшающий декоратор знал бы о пицце, которую он украшает, и это цена. Метод GetPrice() объекта Topping вернет кумулятивную цену как пиццы, так и долива.

ИЗМЕНИТЬ

Вот пример кода выше.

public abstract class BasePizza
{
    protected double myPrice;

    public virtual double GetPrice()
    {
        return this.myPrice;
    }
}

public abstract class ToppingsDecorator : BasePizza
{
    protected BasePizza pizza;
    public ToppingsDecorator(BasePizza pizzaToDecorate)
    {
        this.pizza = pizzaToDecorate;
    }

    public override double GetPrice()
    {
        return (this.pizza.GetPrice() + this.myPrice);
    }
}

class Program
{
    [STAThread]
    static void Main()
    {
        //Client-code
        Margherita pizza = new Margherita();
        Console.WriteLine("Plain Margherita: " + pizza.GetPrice().ToString());

        ExtraCheeseTopping moreCheese = new ExtraCheeseTopping(pizza);
        ExtraCheeseTopping someMoreCheese = new ExtraCheeseTopping(moreCheese);
        Console.WriteLine("Plain Margherita with double extra cheese: " + someMoreCheese.GetPrice().ToString());

        MushroomTopping moreMushroom = new MushroomTopping(someMoreCheese);
        Console.WriteLine("Plain Margherita with double extra cheese with mushroom: " + moreMushroom.GetPrice().ToString());

        JalapenoTopping moreJalapeno = new JalapenoTopping(moreMushroom);
        Console.WriteLine("Plain Margherita with double extra cheese with mushroom with Jalapeno: " + moreJalapeno.GetPrice().ToString());

        Console.ReadLine();
    }
}

public class Margherita : BasePizza
{
    public Margherita()
    {
        this.myPrice = 6.99;
    }
}

public class Gourmet : BasePizza
{
    public Gourmet()
    {
        this.myPrice = 7.49;
    }
}

public class ExtraCheeseTopping : ToppingsDecorator
{
    public ExtraCheeseTopping(BasePizza pizzaToDecorate)
        : base(pizzaToDecorate)
    {
        this.myPrice = 0.99;
    }
}

public class MushroomTopping : ToppingsDecorator
{
    public MushroomTopping(BasePizza pizzaToDecorate)
        : base(pizzaToDecorate)
    {
        this.myPrice = 1.49;
    }
}

public class JalapenoTopping : ToppingsDecorator
{
    public JalapenoTopping(BasePizza pizzaToDecorate)
        : base(pizzaToDecorate)
    {
        this.myPrice = 1.49;
    }
}

Ответ 2

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

// Person object that we will be decorating with logging capability
var person = {
  name: "Foo",
  city: "Bar"
};

// Function that serves as a decorator and dynamically adds the log method to a given object
function MakeLoggable(object) {
  object.log = function(property) {
    console.log(this[property]);
  }
}

// Person is given the dynamic responsibility here
MakeLoggable(person);

// Using the newly added functionality
person.log('name');

Ответ 3

Стоит отметить, что модель Java i/o основана на шаблоне декоратора. Наложение этого читателя на вершину этого читателя поверх... является действительно реальным примером декоратора.

Ответ 4

Пример. Сценарий. Предположим, вы пишете модуль шифрования. Это шифрование может зашифровать прозрачный файл, используя стандарт шифрования DES - Data. Аналогично, в системе вы можете использовать шифрование как стандарт AES - Advance. Кроме того, вы можете иметь комбинацию шифрования - сначала DES, затем AES. Или вы можете иметь сначала AES, затем DES.

Обсуждение. Как вы справитесь с такой ситуацией? Вы не можете продолжать создавать объект таких комбинаций - например, AES и DES - всего 4 комбинации. Таким образом, вам нужно иметь 4 отдельных объекта. Это станет сложным по мере увеличения типа шифрования.

Решение. Продолжайте наращивать комбинации стека в зависимости от необходимости - во время выполнения. Еще одно преимущество этого подхода состоит в том, что вы можете легко расслабиться.

Вот решение - в С++.

Во-первых, вам нужен базовый класс - фундаментальная единица стека. Вы можете считать базой стека. В этом примере это чистый файл. Пусть всегда следует полиморфизм. Сделайте сначала класс интерфейса этого фундаментального устройства. Таким образом, вы можете реализовать его по своему усмотрению. Кроме того, вам не нужно думать о зависимости, включая эту основную единицу.

Вот класс интерфейса -

class IclearData
{
public:

    virtual std::string getData() = 0;
    virtual ~IclearData() = 0;
};

IclearData::~IclearData()
{
    std::cout<<"Destructor called of IclearData"<<std::endl;
}

Теперь реализуем этот класс интерфейса -

class clearData:public IclearData
{
private:

    std::string m_data;

    clearData();

    void setData(std::string data)
        {
            m_data = data;
        }

public:

    std::string getData()
    {
        return m_data;
    }

    clearData(std::string data)
    {
        setData(data);
    }

    ~clearData()
    {
        std::cout<<"Destructor of clear Data Invoked"<<std::endl;
    }

};

Теперь давайте создадим абстрактный класс декоратора, который можно расширить, чтобы создать любые вкусы - здесь аромат - это тип шифрования. Этот абстрактный класс декоратора связан с базовым классом. Таким образом, декоратор "является" видом класса интерфейса ". Таким образом, вам нужно использовать наследование.

class encryptionDecorator: public IclearData
{

protected:
    IclearData *p_mclearData;

    encryptionDecorator()
    {
      std::cout<<"Encryption Decorator Abstract class called"<<std::endl;
    }

public:

    std::string getData()
    {
        return p_mclearData->getData();
    }

    encryptionDecorator(IclearData *clearData)
    {
        p_mclearData = clearData;
    }

    virtual std::string showDecryptedData() = 0;

    virtual ~encryptionDecorator() = 0;

};

encryptionDecorator::~encryptionDecorator()
{
    std::cout<<"Encryption Decorator Destructor called"<<std::endl;
}

Теперь сделаем конкретный класс декоратора - Тип шифрования - AES -

const std::string aesEncrypt = "AES Encrypted ";

class aes: public encryptionDecorator
{

private:

    std::string m_aesData;

    aes();

public:

    aes(IclearData *pClearData): m_aesData(aesEncrypt)
    {
        p_mclearData = pClearData;
        m_aesData.append(p_mclearData->getData());
    }

    std::string getData()
        {
            return m_aesData;
        }

    std::string showDecryptedData(void)
    {
        m_aesData.erase(0,m_aesData.length());
        return m_aesData;
    }

};

Теперь, допустим, тип декоратора DES -

const std::string desEncrypt = "DES Encrypted";

class des: public encryptionDecorator
{

private:

    std::string m_desData;

    des();

public:

    des(IclearData *pClearData): m_desData(desEncrypt)
    {
        p_mclearData = pClearData;
        m_desData.append(p_mclearData->getData());
    }

    std::string getData(void)
        {
            return m_desData;
        }

    std::string showDecryptedData(void)
    {
        m_desData.erase(0,desEncrypt.length());
        return m_desData;
    }

};

Сделайте код клиента для использования этого класса декоратора -

int main()
{
    IclearData *pData = new clearData("HELLO_CLEAR_DATA");

    std::cout<<pData->getData()<<std::endl;


    encryptionDecorator *pAesData = new aes(pData);

    std::cout<<pAesData->getData()<<std::endl;

    encryptionDecorator *pDesData = new des(pAesData);

    std::cout<<pDesData->getData()<<std::endl;

    /** unwind the decorator stack ***/
    std::cout<<pDesData->showDecryptedData()<<std::endl;

    delete pDesData;
    delete pAesData;
    delete pData;

    return 0;
}

Вы увидите следующие результаты -

HELLO_CLEAR_DATA
Encryption Decorator Abstract class called
AES Encrypted HELLO_CLEAR_DATA
Encryption Decorator Abstract class called
DES Encrypted AES Encrypted HELLO_CLEAR_DATA
AES Encrypted HELLO_CLEAR_DATA
Encryption Decorator Destructor called
Destructor called of IclearData
Encryption Decorator Destructor called
Destructor called of IclearData
Destructor of clear Data Invoked
Destructor called of IclearData

Вот диаграмма UML - представление класса. В случае, если вы хотите пропустить код и сфокусироваться на аспекте дизайна.

enter image description here

Ответ 5

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

Лучший пример: классы InputStream и OutputStream в пакете java.io

    File file=new File("target","test.txt");
    FileOutputStream fos=new FileOutputStream(file);
    BufferedOutputStream bos=new BufferedOutputStream(fos);
    ObjectOutputStream oos=new ObjectOutputStream(bos);


    oos.write(5);
    oos.writeBoolean(true);
    oos.writeBytes("decorator pattern was here.");


//... then close the streams of course.

Ответ 6

Что представляет собой шаблон дизайна Decorator в Java.

Формальное определение шаблона Decorator из книги GoF (Design Patterns: Elements of Reusable Object-Oriented Software, 1995, Pearson Education, Inc. Publishing, как Pearson Addison Wesley) говорит, что вы можете

"Прилагайте дополнительные обязанности к объекту динамически. обеспечивают гибкую альтернативу подклассу для расширения функциональности ".

Скажем, у нас есть пицца, и мы хотим украсить ее такими начинками, как Chicken Masala, Onion и Mozzarella Cheese. Посмотрим, как реализовать его в Java...

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

Pizza.java:

<!-- language-all: lang-html -->

package com.hubberspot.designpattern.structural.decorator;

public class Pizza {

public Pizza() {

}

public String description(){
    return "Pizza";
}

}



package com.hubberspot.designpattern.structural.decorator;

public abstract class PizzaToppings extends Pizza {

public abstract String description();

}

package com.hubberspot.designpattern.structural.decorator;

public class ChickenMasala extends PizzaToppings {

private Pizza pizza;

public ChickenMasala(Pizza pizza) {
    this.pizza = pizza;
}

@Override
public String description() {
    return pizza.description() + " with chicken masala, ";
}

}



package com.hubberspot.designpattern.structural.decorator;

public class MozzarellaCheese extends PizzaToppings {

private Pizza pizza;

public MozzarellaCheese(Pizza pizza) {
    this.pizza = pizza;
}

@Override
public String description() {
    return pizza.description() + "and mozzarella cheese.";
}
}



package com.hubberspot.designpattern.structural.decorator;

public class Onion extends PizzaToppings {

private Pizza pizza;

public Onion(Pizza pizza) {
    this.pizza = pizza;
}

@Override
public String description() {
    return pizza.description() + "onions, ";
}

}



package com.hubberspot.designpattern.structural.decorator;

public class TestDecorator {

public static void main(String[] args) {

    Pizza pizza = new Pizza();

    pizza = new ChickenMasala(pizza);
    pizza = new Onion(pizza);
    pizza = new MozzarellaCheese(pizza);

    System.out.println("You're getting " + pizza.description());

}

}

Ответ 7

Я использовал шаблон Decorator в своей работе. Я сделал сообщение в своем блоге о том, как использовать его при регистрации.

Ответ 8

Шаблон декоратора позволяет динамически добавлять поведение к объектам.

Возьмем пример, где вам нужно создать приложение, которое рассчитывает цену на различные виды гамбургеров. Вам нужно обрабатывать различные варианты гамбургеров, таких как "большой" или "с сыром" , каждый из которых имеет цену относительно основного булочки с начинкой. Например. добавьте 10 долларов США для гамбургера с сыром, добавьте дополнительные 15 долларов США для большого гамбургера и т.д.

В этом случае у вас может возникнуть соблазн создать подклассы для их обработки. Мы можем выразить это в Ruby как:

class Burger
  def price
    50
  end
end

class BurgerWithCheese < Burger
  def price
    super + 15
  end
end

В приведенном выше примере класс BurgerWithCheese наследуется от Burger и переопределяет метод цены, чтобы добавить $15 к цене, определенной в суперклассе. Вы также создадите класс LargeBurger и определите цену относительно Burger. Но вам также нужно определить новый класс для комбинации "большой" и "с сыром" .

Теперь, что произойдет, если нам нужно подавать "гамбургер с картофелем фри"? У нас уже есть 4 класса для обработки этих комбинаций, и нам нужно будет добавить еще 4, чтобы обрабатывать все комбинации из 3 свойств - "большой" , "с сыром" и "с картофелем фри". Нам нужно 8 классов. Добавьте еще одно свойство, и нам понадобится 16. Это будет расти как 2 ^ n.

Вместо этого попробуйте определить BurgerDecorator, который принимает объект Burger:

class BurgerDecorator
  def initialize(burger)
    self.burger = burger
  end
end

class BurgerWithCheese < BurgerDecorator
  def price
    self.burger.price + 15
  end
end

burger = Burger.new
cheese_burger = BurgerWithCheese.new(burger)
cheese_burger.price   # => 65

В приведенном выше примере мы создали класс BurgerDecorator, из которого наследуется класс BurgerWithCheese. Мы также можем представить "большую" вариацию, создав класс LargeBurger. Теперь мы можем определить большой гамбургер с сыром во время выполнения:

b = LargeBurger.new(cheese_burger)
b.price  # => 50 + 15 + 20 = 85

Помните, как использование наследования для добавления вариации "с фри" связано с добавлением еще 4 подклассов? С декораторами мы просто создали бы один новый класс, BurgerWithFries, чтобы обрабатывать новый вариант и обрабатывать его во время выполнения. Каждому новому свойству понадобится просто больше декоратора, чтобы покрыть все перестановки.

PS. Это короткая версия статьи, которую я написал о с использованием шаблона Decorator в Ruby, который вы можете прочитать, если хотите узнать более подробные примеры.

Ответ 9

Decorator:

  • Добавить поведение объекта во время выполнения. Наследование - это ключ к достижению этой функциональности, что является одновременно преимуществом и недостатком этого шаблона.
  • Это улучшает поведение интерфейса.
  • Декоратор можно рассматривать как вырожденный композит с одним компонентом. Тем не менее, Decorator добавляет дополнительные обязанности - он не предназначен для агрегации объектов.
  • Класс Decorator объявляет отношение композиции к интерфейсу LCD (Lowest Class Denominator), и этот элемент данных инициализируется в его конструкторе.
  • Decorator предназначен для добавления обязанностей к объектам без подкласса

Подробнее см. sourcemaking.

Decorator (Abstract): это абстрактный класс/интерфейс, который реализует интерфейс компонента. Он содержит компонентный интерфейс. В отсутствие этого класса вам нужно много подклассов ConcreteDecorators для разных комбинаций. Состав компонента уменьшает ненужные подклассы.

Пример JDK:

BufferedInputStream bis = new BufferedInputStream(new FileInputStream(new File("a.txt")));
while(bis.available()>0)
{
        char c = (char)bis.read();
        System.out.println("Char: "+c);;
}

Посмотрите ниже вопрос SE для диаграмм UML и примеров кода.

Рисунок декоратора для IO

Полезные статьи:

journaldev

wikipedia

Пример реального слова шаблона Decorator: VendingMachineDecorator объяснен @

Когда использовать шаблон декоратора?

Beverage beverage = new SugarDecorator(new LemonDecorator(new Tea("Assam Tea")));
beverage.decorateBeverage();

beverage = new SugarDecorator(new LemonDecorator(new Coffee("Cappuccino")));
beverage.decorateBeverage();

В приведенном выше примере чай или кофе (напитки) были украшены сахаром и лимоном.

Ответ 10

Шаблон декоратора достигает единственной цели - динамически добавлять обязанности к любому объекту.

Модель ввода/вывода Java основана на шаблоне декоратора.

Java IO as decorator pattern

Ответ 11

В Википедии есть пример о оформлении окна с помощью прокрутки:

http://en.wikipedia.org/wiki/Decorator_pattern

Вот еще один пример "реального мира": "Член команды, руководитель команды и менеджер", который иллюстрирует, что шаблон декоратора незаменим простым наследованием:

https://zishanbilal.wordpress.com/2011/04/28/design-patterns-by-examples-decorator-pattern/

Ответ 12

Некоторое время назад я реорганизовал кодовую базу в использование шаблона Decorator, поэтому я попытаюсь объяснить пример использования.

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

Все сервисы имеют общий интерфейс

interface Service {
  String serviceId();
  void init() throws Exception;
  void start() throws Exception;
  void stop() throws Exception;
}

Предварительный рефакторинг

abstract class ServiceSupport implements Service {
  public ServiceSupport(String serviceId, LicenseManager licenseManager) {
    // assign instance variables
  }

  @Override
  public void init() throws Exception {
    if (!licenseManager.isLicenseValid(serviceId)) {
       throw new Exception("License not valid for service");
    }
    // Service initialization logic
  }
}

Если вы внимательно наблюдаете, ServiceSupport зависит от LicenseManager. Но почему это должно зависеть от LicenseManager? Что делать, если нам нужен фоновый сервис, который не должен проверять информацию о лицензии. В текущей ситуации нам придется как-то обучить LicenseManager возвращать true для фоновых сервисов. Этот подход мне не показался удачным. По моему мнению проверка лицензии и другая логика были ортогональны друг другу.

Таким образом, на помощь приходит Decorator Pattern и здесь начинается рефакторинг с TDD.

Постфакторинг

class LicensedService implements Service {
  private Service service;
  public LicensedService(LicenseManager licenseManager, Service service) {
    this.service = service;
  }

  @Override
  public void init() {
    if (!licenseManager.isLicenseValid(service.serviceId())) {
      throw new Exception("License is invalid for service " + service.serviceId());
    }
    // Delegate init to decorated service
    service.init();
  }

  // override other methods according to requirement
}

// Not concerned with licensing any more :)
abstract class ServiceSupport implements Service {
  public ServiceSupport(String serviceId) {
    // assign variables
  }

  @Override
  public void init() {
    // Service initialization logic
  }
}

// The services which need license protection can be decorated with a Licensed service
Service aLicensedService = new LicensedService(new Service1("Service1"), licenseManager);
// Services which don't need license can be created without one and there is no need to pass license related information
Service aBackgroundService = new BackgroundService1("BG-1");

Takeaways

  • Сплоченность кода стала лучше
  • Модульное тестирование стало проще, так как не нужно проверять лицензирование при тестировании ServiceSupport
  • Не нужно обходить лицензирование какими-либо специальными проверками фоновых сервисов
  • Правильное разделение обязанностей

Ответ 13

Давайте возьмем пример PubG. Штурмовые винтовки лучше всего работают с 4-кратным зумом, и пока мы на нем, нам также понадобятся компенсатор и подавитель. Это уменьшит отдачу и уменьшит звук стрельбы, а также эхо. Нам нужно будет реализовать эту функцию, где мы позволим игрокам покупать их любимые ружья и аксессуары. Игроки могут купить пистолет или часть аксессуара или весь аксессуар, и с них будет взиматься соответствующая плата.

Давайте посмотрим, как шаблон декоратора применяется здесь:

Предположим, кто-то хочет купить SCAR-L со всеми тремя аксессуарами, упомянутыми выше.

  1. Взять предмет SCAR-L
  2. Украсьте (или добавьте) СКАР-Л с 4-кратным увеличением
  3. Украсьте СКАР-Л объектом-подавителем
  4. Украсьте СКАР-Л компрессором
  5. Вызовите метод стоимости и позвольте каждому делегату объекта добавить стоимость, используя метод стоимости аксессуаров.

Это приведет к диаграмме классов, подобной этой:

Decorator pattern at work

Теперь у нас могут быть такие классы:

public abstract class Gun {     
    private Double cost;    
    public Double getCost() {           
        return cost;        
       }    
    }

public abstract class GunAccessories extends Gun {  }

public class Scarl extends Gun {    
    public Scarl() {            
        cost = 100;
        }   
     }

public class Suppressor extends GunAccessories {        
    Gun gun;        
    public Suppressor(Gun gun) {            
    cost = 5;           
    this.gun = gun;     
    }               
    public double getCost(){            
        return cost + gun.getCost();
    }
}

public class GunShop{   
    public static void main(String args[]){         
    Gun scarl = new Scarl();                
    scarl = new Supressor(scarl);
    System.out.println("Price is "+scarl.getCost());
    }      
}

Мы также можем добавить другие аксессуары и украсить наш пистолет.

Ссылка:

https://nulpointerexception.com/2019/05/05/a-beginner-guide-to-decorator-pattern/

Ответ 14

шаблон оформления декоратора: Этот шаблон помогает изменять характеристики объекта во время выполнения. Он обеспечивает различные вкусы для объекта и дает гибкость в выборе ингредиентов, которые мы хотим использовать в этом аромате.

Пример реальной жизни: Допустим, у вас есть основное сиденье в полете. Теперь вам разрешено выбирать несколько удобств с сиденьем. У каждого удобства есть своя стоимость, связанная с этим. Теперь, если пользователь выбирает Wifi и премиальную пищу, он/она будет взиматься за питание + wifi + premium.

введите описание изображения здесь

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