You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

装饰器设计模式示例:我的实现是否正确?如何优化字段装饰?

关于装饰器模式的问题解答

你的当前实现是否正确?

答案是不正确,核心问题集中在这几点:

  • 违背了装饰器模式「通过包装对象增强功能,不修改原有对象结构」的核心思想,你的代码直接修改了原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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 23:33:19