我正在研究GOF中记录的Decorator Pattern。
请帮我了解装饰器模式。有人可以举一个在现实世界中有用的用例示例吗?
我正在研究GOF中记录的Decorator Pattern。
请帮我了解装饰器模式。有人可以举一个在现实世界中有用的用例示例吗?
Answers:
装饰器模式实现了向任何对象动态添加职责的单一目标。
考虑一个比萨店的情况。在比萨店,他们将出售少量比萨饼品种,并且还将在菜单中提供浇头。现在设想一种情况,如果比萨店必须提供比萨饼和浇头的每种组合的价格。即使有四个基本的比萨饼和8个不同的浇头,应用程序也会疯狂地维护所有这些比萨饼和浇头的具体组合。
装饰器模式来了。
按照装饰器模式,您将实现装饰物的浇头,而比萨饼将由这些装饰物的装饰器装饰。实际上,每个客户都希望自己想要的浇头,最终的账单金额将由基础比萨和额外订购的浇头组成。每个浇头装饰者都会知道它正在装饰的比萨饼及其价格。Topping对象的GetPrice()方法将返回比萨饼和浇头的累计价格。
这是上面说明的代码示例。
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 I / O模型基于装饰器模式。该阅读器在...之上的分层结构是装饰器的真实示例。
示例-场景-假设您正在编写一个加密模块。这种加密可以使用DES-数据加密标准对透明文件进行加密。同样,在系统中,您可以将加密作为AES-高级加密标准进行。另外,您可以结合使用加密-首先是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加密的”;
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
吗?
装饰器模式通过与该对象的其他类似子类链接来帮助您更改或配置对象的功能。
最好的示例是java.io包中的InputStream和OutputStream类
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中的装饰器设计模式。
GoF书中的Decorator模式的正式定义(设计模式:可重用的面向对象软件的元素,1995年,Pearson Education,Inc.以Pearson Addison Wesley的身份出版)说,
“将额外的责任动态地附加到对象上。装饰器为子类提供了灵活的替代方案,以扩展功能。”
假设我们有一个披萨,我们想用诸如Masala鸡肉,洋葱和Mozzarella奶酪这样的配料来装饰它。让我们看看如何在Java中实现它...
演示如何在Java中实现Decorator Design Pattern的程序。
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());
}
}
装饰器模式使您可以动态地向对象添加行为。
让我们以一个示例为例,您需要构建一个应用程序来计算各种汉堡的价格。您需要处理各种不同的汉堡,例如“大号”或“配奶酪”,每种汉堡的价格都相对于基本汉堡。例如,为汉堡加奶酪增加10美元,为大汉堡增加15美元,等等。
在这种情况下,您可能会想创建子类来处理这些子类。我们可以用Ruby表示为:
class Burger
def price
50
end
end
class BurgerWithCheese < Burger
def price
super + 15
end
end
在上面的示例中,BurgerWithCheese类继承自Burger,并且覆盖了price方法,将$ 15添加到超类中定义的价格上。您还将创建LargeBurger类并定义相对于Burger的价格。但是,您还需要为“大”和“加奶酪”的组合定义一个新类。
现在,如果我们需要为“汉堡配薯条”服务会发生什么?我们已经有4个类来处理这些组合,并且我们将需要再添加4个类来处理3个属性的所有组合-“大”,“配奶酪”和“配薯条”。我们现在需要8节课。添加另一个属性,我们将需要16。这将增长为2 ^ n。
相反,让我们尝试定义一个接受Burger对象的BurgerDecorator:
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。这是我写的有关在Ruby中使用Decorator Pattern的文章的简短版本,如果您想查找更详细的示例,可以阅读。
装饰器:
有关更多详细信息,请参阅源制作文章。
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图和代码示例。
有用的文章:
Decorator模式的实词示例:VendingMachineDecorator已在@中进行了解释
Beverage beverage = new SugarDecorator(new LemonDecorator(new Tea("Assam Tea")));
beverage.decorateBeverage();
beverage = new SugarDecorator(new LemonDecorator(new Coffee("Cappuccino")));
beverage.decorateBeverage();
在上面的示例中,茶或咖啡(饮料)已用糖和柠檬装饰。
维基百科上有一个有关使用滚动条装饰窗口的示例:
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
后台服务。这种方法在我看来并不好。根据我的说法,许可证检查和其他逻辑是相互正交的。
所以装饰图案可以解决,并开始使用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());
}
}
我们也可以类似地添加其他配件并装饰我们的Gun。
参考:
https://nulpointerexception.com/2019/05/05/a-beginner-guide-to-decorator-pattern/
装饰器设计模式:此模式有助于在运行时修改对象的特征。它为物体提供了不同的风味,并提供了灵活性,可以选择我们要在该风味中使用的成分。
现实生活中的例子:假设您在飞机上有一个主舱座位。现在,您可以在座椅上选择多种便利设施。每个便利设施都有其相关的成本。现在,如果用户选择Wifi和优质食物,则需要向他/她收取座位+ wifi +优质食物的费用。
在这种情况下,装饰器设计模式确实可以为我们提供帮助。请访问上面的链接,以了解有关装饰器模式和一个实际示例的实现的更多信息。