装饰器设计模式示例:我的实现是否正确?如何优化字段装饰?
关于装饰器模式的问题解答
你的当前实现是否正确?
答案是不正确,核心问题集中在这几点:
- 违背了装饰器模式「通过包装对象增强功能,不修改原有对象结构」的核心思想,你的代码直接修改了原
Fields对象的InvoiceNumber属性,破坏了原对象的纯净性。 - 装饰器与
Fields类耦合过紧,无法复用在其他类似对象上,不符合开闭原则。 - 调用方式不支持灵活组合多个装饰器的效果,失去了装饰器模式的扩展性优势。
装饰字段值的最佳方式 & 改进方案
正确的装饰器模式需要遵循「抽象组件-具体组件-抽象装饰器-具体装饰器」的结构,通过包装原对象动态增强属性,而非直接修改原对象。下面是具体的改进步骤和代码示例:
步骤1:定义抽象组件(Component)
先创建一个接口,规范需要装饰的属性,让装饰器基于接口扩展,降低耦合:
public interface IInvoiceFields { string InvoiceNumber { get; set; } DateTime InvoiceDate { get; set; } DateTime DueDate { get; set; } string ReferenceNumber { get; set; } string OrganisationNumber { get; set; } }
步骤2:实现具体组件(Concrete Component)
让原Fields类实现这个接口:
public class Fields : IInvoiceFields { public string InvoiceNumber { get; set; } public DateTime InvoiceDate { get; set; } public DateTime DueDate { get; set; } public string ReferenceNumber { get; set; } public string OrganisationNumber { get; set; } }
步骤3:定义抽象装饰器(Decorator)
创建抽象装饰器类,实现IInvoiceFields接口,同时持有一个IInvoiceFields实例(被装饰的对象),默认直接转发原对象的属性:
public abstract class InvoiceFieldsDecorator : IInvoiceFields { protected readonly IInvoiceFields _invoiceFields; public InvoiceFieldsDecorator(IInvoiceFields invoiceFields) { _invoiceFields = invoiceFields; } // 默认转发原对象的属性,子类可按需重写 public virtual string InvoiceNumber { get => _invoiceFields.InvoiceNumber; set => _invoiceFields.InvoiceNumber = value; } public virtual DateTime InvoiceDate { get => _invoiceFields.InvoiceDate; set => _invoiceFields.InvoiceDate = value; } public virtual DateTime DueDate { get => _invoiceFields.DueDate; set => _invoiceFields.DueDate = value; } public virtual string ReferenceNumber { get => _invoiceFields.ReferenceNumber; set => _invoiceFields.ReferenceNumber = value; } public virtual string OrganisationNumber { get => _invoiceFields.OrganisationNumber; set => _invoiceFields.OrganisationNumber = value; } }
步骤4:实现具体装饰器(Concrete Decorator)
创建具体装饰器,重写InvoiceNumber的getter来返回装饰后的值:
// 给发票号添加++的装饰器 public class InvoiceNumberPlusDecorator : InvoiceFieldsDecorator { public InvoiceNumberPlusDecorator(IInvoiceFields invoiceFields) : base(invoiceFields) { } public override string InvoiceNumber { get => $"{_invoiceFields.InvoiceNumber}++"; set => _invoiceFields.InvoiceNumber = value; } } // 给发票号添加--的装饰器 public class InvoiceNumberMinusDecorator : InvoiceFieldsDecorator { public InvoiceNumberMinusDecorator(IInvoiceFields invoiceFields) : base(invoiceFields) { } public override string InvoiceNumber { get => $"{_invoiceFields.InvoiceNumber}--"; set => _invoiceFields.InvoiceNumber = value; } }
步骤5:使用装饰器
现在可以通过链式包装灵活组合装饰器,原对象不会被直接修改:
// 创建原对象 IInvoiceFields fields = new Fields(); fields.InvoiceNumber = "1234"; // 用++装饰器包装 IInvoiceFields decoratedFields1 = new InvoiceNumberPlusDecorator(fields); Console.WriteLine(decoratedFields1.InvoiceNumber); // 输出:1234++ // 叠加--装饰器 IInvoiceFields decoratedFields2 = new InvoiceNumberMinusDecorator(decoratedFields1); Console.WriteLine(decoratedFields2.InvoiceNumber); // 输出:1234++-- // 原对象的InvoiceNumber依然保持初始值 Console.WriteLine(fields.InvoiceNumber); // 输出:1234
这种实现的优势
- 原对象保持纯净,装饰逻辑独立于原对象,避免了直接修改带来的副作用。
- 支持灵活组合多个装饰器,新增装饰器无需修改原有代码,完全符合开闭原则。
- 基于接口实现,耦合度低,装饰器可以复用在其他实现了
IInvoiceFields的类上。
内容的提问来源于stack exchange,提问作者mrd
相关产品推荐
相关产品推荐

