用实际软件编程示例解释 C# 中的装饰器模式






4.28/5 (11投票s)
本文通过一个实际的软件编程示例演示了 C# 中装饰器模式的用法。
背景
GoF 定义:动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式比生成子类实现更为灵活。
此模式支持 SOLID 的开放/封闭原则,这意味着您可以在不修改现有类的情况下向其添加新功能。您可以使用继承或组合向现有类添加新功能。
让我们考虑一个实际的软件编程示例。
我的应用程序中有一个发票模块,用于打印简单的客户发票。此发票包含文本数据,如客户姓名、地址、订单号、订单金额和订单日期。多年来,我的许多客户都使用此功能。
现在,我的一个客户(客户 A)希望打印带有颜色的发票。第二个客户(客户 B)希望打印带有标题的发票。第三个客户(客户 C)希望打印带有页脚的发票。那么,您认为我该如何满足这一需求?继承?让我们试试。
通过继承,我将得到上面的类。我将为每个新功能创建一个子类。到目前为止似乎还不错。但是,当一个新客户(客户 D)希望同时打印带有标题和页脚的发票时,上述场景将变得复杂。如果我们想实现客户 D 的要求,我们的结构可能如下所示
等等,这是多重继承。我在 C# 中不能这样做。我无法从以上两个类实现。因此,现在我们将不得不创建一个新的子类,它将打印带有标题和页脚的发票,并满足客户 D 的要求。
上述子类方法存在两个问题
- 你最终会得到很多子类。确切地说,每个组合都有一个子类,例如标题 + 页脚是一个组合。另一个组合可以是颜色 + 标题...等等。在大型系统中,维护和调试大量子类将很困难。
- 如果您遵循 SOLID 的单一职责原则,它指出一个类应该只处理一个功能部分。因此,我们的子类不应处理组合任务。 这意味着一个类应该添加颜色,而另一个类应该添加标题信息。
为了克服上述问题,使用了装饰器模式。
使用装饰器模式,您可以在不影响现有类的情况下附加新功能。它为扩展现有类提供了一种替代子类化的方法。
在上面的示例中,InvoiceBase
和 Invoice
类是现有类。我需要添加的任何新功能都将是一个 Decorator
。我们可以将多个装饰器附加到我们的现有类,而无需为每个组合创建单独的子类。我创建了一个 abstract
类 InvoiceDecorator
和三个额外的类,ColorDecorator
、HeaderDecorator
和 FooterDecorator
。InvoiceDecorator
具有 InvoiceBase
对象的组合。当我们想向现有功能添加新功能时,我们使用 AttachTo
方法。这样做的想法是在将来添加一个新的装饰器,而不会影响我现有的 Invoice
类。
我创建了一个示例应用程序,模拟发票打印操作。
Using the Code
以下是我的现有类,在添加新功能时不会修改它们。
// 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
类。我们可以添加任意数量的装饰器来扩展功能。另请注意,我按照 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();
关注点
很多时候,在处理客户需求时,您没有完整的需求,而是增量开发。此模式可用于此类场景。您可以根据新需求添加装饰器,从而确保您的基类首先不复杂。
历史
- 2020 年 4 月 10 日 - 初始版本