Я изучал шаблон декоратора как документировано в GOF .
Пожалуйста, помогите мне понять Шаблон Декоратора . Может ли кто-нибудь привести пример использования того, где это полезно в реальном мире?
Я изучал шаблон декоратора как документировано в GOF .
Пожалуйста, помогите мне понять Шаблон Декоратора . Может ли кто-нибудь привести пример использования того, где это полезно в реальном мире?
Ответы:
Шаблон декоратора достигает единственной цели - динамически добавлять обязанности к любому объекту.
Рассмотрим случай пиццерии. В магазине пиццы они продадут несколько сортов пиццы, а также предоставят начинки в меню. А теперь представьте ситуацию, когда в магазине пиццы нужно указывать цены на каждую комбинацию пиццы и топпинга. Даже если есть четыре основные пиццы и 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;
}
}
Это простой пример динамического добавления нового поведения к существующему объекту или шаблону 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');
switch
или просто if
, вы могли бы утверждать, что это отличный пример динамического добавления поведения в класс. НО, нам нужно как минимум два класса, чтобы определить декоратор и декорированные объекты в этом шаблоне.
Стоит отметить, что модель ввода-вывода Java основана на шаблоне декоратора. Слой этого читателя поверх этого читателя поверх ... - это действительно реальный пример декоратора.
Пример - Сценарий. Допустим, вы пишете модуль шифрования. Это шифрование может зашифровать чистый файл, используя DES - стандарт шифрования данных. Аналогично, в системе вы можете использовать шифрование как стандарт шифрования AES - Advance. Также у вас может быть комбинация шифрования - сначала DES, затем AES. Или вы можете иметь сначала AES, потом DES.
Обсуждение - Как вы будете обслуживать эту ситуацию? Вы не можете продолжать создавать объект таких комбинаций - например - AES и DES - всего 4 комбинации. Таким образом, вам нужно иметь 4 отдельных объекта. Это будет усложняться по мере увеличения типа шифрования.
Решение - Продолжайте наращивать стек - комбинации в зависимости от необходимости - во время выполнения. Еще одно преимущество этого стекового подхода заключается в том, что вы можете легко его раскрутить.
Вот решение - в C ++.
Во-первых, вам нужен базовый класс - фундаментальная единица стека. Вы можете думать как основа стека. В этом примере это чистый файл. Будем всегда следовать полиморфизму. Сделайте сначала интерфейсный класс этой фундаментальной единицы. Таким образом, вы можете реализовать это как хотите. Кроме того, вам не нужно думать о зависимости при включении этого фундаментального модуля.
Вот класс интерфейса -
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 - Классовое представление этого. В случае, если вы хотите пропустить код и сосредоточиться на аспекте дизайна.
strategy pattern
?
Шаблон Decorator поможет вам изменить или настроить функциональность вашего объекта, связав его с другими подобными подклассами этого объекта.
Лучший пример - классы 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.
Что такое шаблон оформления декоратора в Java.
Формальное определение шаблона Decorator из книги GoF (Шаблоны проектирования: элементы многоразового объектно-ориентированного программного обеспечения, 1995, Pearson Education, Inc. Publishing as Pearson Addison Wesley) говорит, что вы можете,
«Прикрепите дополнительные обязанности к объекту динамически. Декораторы предоставляют гибкую альтернативу подклассам для расширения функциональности».
Допустим, у нас есть пицца, и мы хотим украсить ее такими начинками, как куриный масала, лук и сыр моцарелла. Давайте посмотрим, как реализовать это в Java ...
Программа для демонстрации того, как реализовать Pattern Design 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());
}
}
Я широко использовал шаблон Decorator в своей работе. Я сделал пост в своем блоге о том, как использовать его с ведением журнала.
Шаблон декоратора позволяет динамически добавлять поведение к объектам.
Давайте рассмотрим пример, в котором вам нужно создать приложение, которое рассчитывает цену различных видов гамбургеров. Вам нужно обрабатывать разные вариации гамбургеров, например, «большой» или «с сыром», каждый из которых имеет цену относительно основного гамбургера. Например, добавьте 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 , которую я написал , которую вы можете прочитать, если хотите найти более подробные примеры.
Decorator:
Обратитесь к источникам статье для более подробной информации.
Декоратор (Аннотация) : это абстрактный класс / интерфейс, который реализует интерфейс компонента. Содержит интерфейс компонента. В отсутствие этого класса вам нужно много подклассов ConcreteDecorator для разных комбинаций. Композиция компонента уменьшает ненужные подклассы.
Пример 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-диаграммы и примеров кода.
Полезные статьи:
Пример настоящего слова шаблона Decorator: VendingMachineDecorator @
Когда использовать шаблон декоратора?
Beverage beverage = new SugarDecorator(new LemonDecorator(new Tea("Assam Tea")));
beverage.decorateBeverage();
beverage = new SugarDecorator(new LemonDecorator(new Coffee("Cappuccino")));
beverage.decorateBeverage();
В приведенном выше примере чай или кофе (напиток) украшены сахаром и лимоном.
Шаблон декоратора достигает единственной цели - динамически добавлять обязанности к любому объекту .
Модель ввода / вывода Java основана на шаблоне декоратора.
В Википедии есть пример оформления окна с помощью полосы прокрутки:
http://en.wikipedia.org/wiki/Decorator_pattern
Вот еще один «реальный мир» пример «члена команды, руководителя и менеджера», который показывает, что шаблон декоратора незаменим при простом наследовании:
https://zishanbilal.wordpress.com/2011/04/28/design-patterns-by-examples-decorator-pattern/
Некоторое время назад я реорганизовал кодовую базу в использование шаблона 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");
Давайте возьмем пример PubG. Штурмовые винтовки лучше всего работают с 4-кратным зумом, и пока мы на нем, нам также понадобятся компенсатор и подавитель. Это уменьшит отдачу и уменьшит звук стрельбы, а также эхо. Нам нужно будет реализовать эту функцию, где мы позволим игрокам покупать их любимые ружья и аксессуары. Игроки могут купить пистолет или часть аксессуара или весь аксессуар, и с них будет взиматься соответствующая плата.
Давайте посмотрим, как шаблон декоратора применяется здесь:
Предположим, кто-то хочет купить SCAR-L со всеми тремя аксессуарами, упомянутыми выше.
Это приведет к такой диаграмме классов:
Теперь у нас могут быть такие классы:
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/
Шаблон Design Decorator : этот шаблон помогает изменять характеристики объекта во время выполнения. Он обеспечивает объектам различные вкусы и дает возможность выбирать, какие ингредиенты мы хотим использовать в этом вкусе.
Пример из реальной жизни: допустим, у вас есть основное место в салоне самолета. Теперь вам разрешено выбирать несколько удобств с места. Каждое удобство имеет свою цену, связанную с этим. Теперь, если пользователь выбирает Wi-Fi и премиум-питание, с него / нее взимается плата за место + Wi-Fi + премиум-питание.
В этом случае дизайн шаблона декоратора действительно может нам помочь. Посетите приведенную выше ссылку, чтобы узнать больше о шаблоне декоратора и реализации одного реального примера.