目录
背景
使用代码
兴趣点
- 下载演示-5.6 KB
GoF定义:动态将附加职责附加到对象。装饰器为子类提供了灵活的替代方案,以扩展功能。
此模式支持SOLID 的“开-闭”原理,这意味着您可以将新功能添加到现有类中,而无需对其进行修改。您可以使用继承或组合向现有类添加新功能。
让我们考虑一个实际的软件编程示例。
我的应用程序中有一个发票模块,可打印简单的客户发票。该发票包含文本数据,例如客户名称、地址、订单号、订单金额和订单日期。数年来,我的几个客户一直使用此功能。
现在,我的一位客户(客户A)希望使用Colors打印发票。第二个客户端(客户端B)要打印发票Headers。第三个客户(客户C)希望使用Footers打印发票。那么,您认为我如何满足这一要求?继承?让我们尝试一下。
通过继承,我将以上述类结束。我将为每个新功能创建一个子类。到目前为止似乎还不错。但是,但是,当一个新客户端(客户端D)想要同时打印页眉和页脚时,上述场景会产生复杂性。如果我们想满足客户D的要求,我们的结构可能如下所示:
哦,等等,这是多重继承。我在C#中无法做到这一点。我不能从以上两个类实现。因此,现在我们将不得不创建一个新的子类,该子类将打印带有页眉和页脚的发票,并满足客户D的要求。
上述子分类方法存在两个问题:
- 您最终会遇到许多子类。确切地说,每种组合(如页眉+页脚)的一个子类就是一种组合。另一个组合可以是“颜色+标题...”等等。在大型系统中,很难维护和调试许多子类。
- 如果您遵循SOLID的“单一职责原则”,则说明类应该处理功能的单个部分。因此,我们的子类不应处理任务的组合。这意味着一类应该添加颜色,而另一类应该添加页眉信息。
为了克服上述问题,使用了装饰器模式。
使用Decorator模式,可以在不影响现有类的情况下附加新功能。它为扩展现有类提供了子类替代方法。
在上面的示例中,InvoiceBase和Invoice类是现有的类。我需要添加的任何新功能都将是Decorator。我可以将多个装饰器附加到我们现有的类中,而无需为每个组合创建单独的子类。我创建了一个abstract类InvoiceDecorator和另外三个类ColorDecorator,HeaderDecorator和FooterDecorator。InvoiceDecorator具有InvoiceBase对象的组成。当我们想向现有功能添加新功能时,我们使用AttachTo 方法。其背后的想法是将来在不影响我现有Invoice类的情况下添加新的装饰器。
我创建了一个示例应用程序,可以模拟发票打印操作。
使用代码以下是我现有的类,在添加新功能时将不会对其进行修改。
// This is the base abstract class which is inherited
// by all concrete and decorator classes
abstract class InvoiceBase
{
// string data stores the content which it to be printed in the invoice.
// This variable will be altered by the subclasses. Thus, it is marked protected.
protected static string data;
// This function will be implemented in all the concrete classes as well as decorators
public abstract void CreateInvoice();
public void PrintInvoice()
{
Console.WriteLine(data);
}
//This function clear the variable value after invoice is printed.
public void Reset()
{
data = string.Empty;
}
}
public class Invoice : InvoiceBase
{
public override void CreateInvoice()
{
data += "\n";
data += "\tCustomer Name : Chris\n";
data += "\tCustomer Address : Edmond Road, NJ\n";
data += "\tOrder No : 1254158\n";
data += "\tOrder Amount : Rs. 2540/- \n";
data += "\tOrder Date : 09-Apr-2020 \n";
data += "\n";
}
}
以下是Decorator与分离的具体实现类。我们可以添加任意数量的Decorator来扩展功能。另外,请注意,我已经按照SOLID原则为一个职责创建了一个类,即颜色处理由ColorDecorater类负责,页眉信息处理由HeaderDecorator类负责。
// This is the base Decorator class which has the composition i.e. InvoiceBase object
// AttachTo method is used to attach responsibility dynamically through client code.
abstract class InvoiceDecorator : InvoiceBase
{
InvoiceBase invoiceBase;
public void AttachTo(InvoiceBase invoice)
{
this.invoiceBase = invoice;
}
public override void CreateInvoice()
{
invoiceBase.CreateInvoice();
}
}
//These are individual decorator classes.
class ColorDecorator : InvoiceDecorator
{
public override void CreateInvoice()
{
AddColor();
base.CreateInvoice();
}
private void AddColor()
{
Console.ForegroundColor = ConsoleColor.Green;
}
}
class HeaderDecorator : InvoiceDecorator
{
public override void CreateInvoice()
{
AddHeader();
base.CreateInvoice();
}
private void AddHeader()
{
string header = "\n\tBlue Heaven Inc.\n"
+ "\tBay Area, NC\n"
+ "\t+1 784251485\n\n";
data = header + data;
}
}
class FooterDecorator : InvoiceDecorator
{
public override void CreateInvoice()
{
base.CreateInvoice();
AddFooter();
}
void AddFooter()
{
string footer = "\n\tCopyright @ 2020.All rights reserved\n";
data += footer;
}
}
客户端代码如下所示:
// CASE 1: This is the existing implementation to print a simple invoice.
InvoiceBase invoice = new Invoice();
invoice.CreateInvoice();
invoice.PrintInvoice();
// CASE 2: Add color to the invoice
InvoiceBase invoice = new Invoice();
InvoiceDecorator colorDecorator = new ColorDecorator();
colorDecorator.AttachTo(invoice);
colorDecorator.CreateInvoice();
invoice.PrintInvoice();
// CASE 3: Add Color, Header and Footer to the invoice
InvoiceBase invoice = new Invoice();
InvoiceDecorator colorDecorator = new ColorDecorator();
InvoiceDecorator headerDecorator = new HeaderDecorator();
InvoiceDecorator footerDecorator = new FooterDecorator();
colorDecorator.AttachTo(invoice);
footerDecorator.AttachTo(colorDecorator);
headerDecorator.AttachTo(footerDecorator);
headerDecorator.CreateInvoice();
invoice.PrintInvoice();
兴趣点
在处理客户需求时,很多时候您没有完整的需求,而是逐步进行开发。可以在这种情况下使用此模式。您可以根据新要求添加Decorators,从而确保您的Base类首先不复杂。