【问题标题】:Hierarchical Data - Composite Pattern C#分层数据 - 复合模式 C#
【发布时间】:2017-03-15 11:38:14
【问题描述】:

我在一些我必须生成的报告中看到了一个反复出现的模式。这些报告是基于日期范围的报告,需要按不同级别汇总。

为简单起见,我们假设此报告在叶节点级别(最低级别)生成标题和配额。在行项目级别(这是各种叶节点的集合),我想汇总配额并提供单独的标题。这些订单项将进一步汇总到另一个级别,这将再次汇总配额并具有唯一的标题。

所以报告会是这样的:

ROOT LEVEL | Title = "Main Report" | Quota = 100
   Month Level    | Title = "Jan" | Quota = 100
     Week Level   | Title = "Week 1" | Quota = 25
     Week Level   | Title = "Week 2" | Quota = 75

我有没有办法使用复合模式来构建它?我尝试了很多方法。他们中的大多数都达不到要求,因为我无法有效地将配额汇总/汇总到更高的级别。

我可以像这样构建一个界面:

public interface IInventoryReportItem
{ 
    string Title { get; set; }
    int Quota { get; set; }
}

然后我可以像这样构建一个订单项:

public class LineItem : IInventoryReportItem

我也可以像这样建立一个集合:

 public class LineItems : IList<IInventoryReportItem>, IInventoryReportItem
{
    private readonly List<IInventoryReportItem> _subLineItems;

    public LineItems()
    {
        _subLineItems = new List<IInventoryReportItem>();
    }

我的报告会是这样的:

public class InventoryReport
{
    public DateRange DateRange { get; set; }
    public LineItems LineItems { get; set; }

}

我现在可以轻松地以分层方式构建报表,但我仍然需要从外部调用聚合函数,而不是它为我自动计算:

var report = new InventoryReport();

        var week1Days = new LineItems
        {
            new LineItem {Quota = 20, Title = "Day 1"},
            new LineItem {Quota = 10, Title = "Day 2"}
        };

        var week2Days = new LineItems
        {
            new LineItem {Quota = 10, Title = "Day 1"},
            new LineItem {Quota = 10, Title = "Day 2"}
        };

        var week1 = new LineItems {week1Days};
        week1.Quota = week1.Sum(x => x.Quota);
        week1.Title = "Week1";


        var week2 = new LineItems {week2Days};
        week2.Quota = week2.Sum(x => x.Quota);
        week2.Title = "Week2";

        var month1 = new LineItems(new List<IInventoryReportItem> {week1, week2});
        month1.Title = "January";
        month1.Quota = month1.Sum(x => x.Quota);

        report.LineItems = new LineItems(new List<IInventoryReportItem> {month1});

有没有办法让我仍然可以灵活地添加单个订单项或一系列项目,并且它还可以使用组合自动计算/聚合数据?

任何帮助都会很棒!

谢谢你, 阿努普

【问题讨论】:

  • LineItems' Quota 属性实现一个getter,检查其自身的长度,如果大于0,则对其子Quotas 求和。我不确定这是否符合“复合模式”,但我不会让模式妨碍有效的代码。

标签: c# design-patterns


【解决方案1】:

对我来说,您似乎正在寻找 RX(反应式扩展),因此您不必每次在每个级别手动求和。相反,只需设置必要的订阅并自动重新计算。例如: Good example of Reactive Extensions Use

【讨论】:

  • 您好,在连接的情况下会很好。这是我想以 JSON 格式发送的 api 的背后。
【解决方案2】:

我解决了这个问题。对于那些感兴趣的人,这是我解决它的方法:

我搭建了一个如图所示的界面:

public interface IInventoryReportItem
{
    string Title { get; set; }
    int Quota { get; }
    int TotalTicketsSold { get; }
    int TotalUnitsSold { get; }
    decimal TotalSalesAmount { get; }
}

我在一个名为 LineItem 的类中实现了这个接口,如下所示:

public class LineItem : IInventoryReportItem
{

    public LineItem(string title, int quota, int totalTicketsSold, int totalUnitsSold, int totalCheckedIn,
        decimal totalSalesAmount)
    {
        Title = title;
        Quota = quota;
        TotalUnitsSold = totalUnitsSold;
        TotalTicketsSold = totalTicketsSold;
        TotalCheckedIn = totalCheckedIn;
        TotalSalesAmount = totalSalesAmount;
    }

    public string Title { get; set; }
    public int Quota { get; }
    public int TotalTicketsSold { get; }
    public int TotalUnitsSold { get; }
    public int TotalCheckedIn { get; }
    public decimal TotalSalesAmount { get; }

}

我还创建了一个名为 LineItems 的自定义集合类,如图所示。请注意,该集合本身属于 IInventoryReportItem 类型:

public class LineItems : IInventoryReportItem
{
    public string Title { get; set; }
    public int Quota => Contents?.Sum(x => x.Quota) ?? 0;
    public int TotalTicketsSold => Contents?.Sum(x => x.TotalTicketsSold) ?? 0;
    public int TotalUnitsSold => Contents?.Sum(x => x.TotalUnitsSold) ?? 0;
    public decimal TotalSalesAmount => Contents?.Sum(x => x.TotalSalesAmount) ?? 0;

    public readonly List<IInventoryReportItem> Contents;

    public LineItems(List<IInventoryReportItem> lineItems)
    {
        Contents = lineItems ?? new List<IInventoryReportItem>();
    }

}

所有的聚合都是在这个集合类级别完成的。

报表类如下:

public class InventoryReport
{
    public DateRange DateRange { get; set; }
    public IInventoryReportItem LineItems { get; set; }

}

然后我可以像这样构建报告:

 Report = new InventoryReport();
        var week1 = new LineItems(new List<IInventoryReportItem>
        {
            new LineItem("Day1", 10, 10, 10, 4, 100),
            new LineItem("Day2", 10, 5, 5, 1, 50)
        })
        {Title = "Week1"};

        var week2 = new LineItems(new List<IInventoryReportItem>
        {
            new LineItem("Day1", 20, 20, 20, 20, 200),
            new LineItem("Day2", 20, 5, 5, 5, 50)
        }) {Title = "Week2"};

        var month1 = new LineItems(new List<IInventoryReportItem> {week1, week2}) {Title = "January"};
        Report.LineItems = new LineItems(new List<IInventoryReportItem> {month1}) {Title = "Daily Report"};

我从 API 收到的最终输出 (JSON) 如下所示:

{
"lineItems": {
"contents": [
  {
    "contents": [
      {
        "contents": [
          {
            "title": "Day1",
            "quota": 10,
            "totalTicketsSold": 10,
            "totalUnitsSold": 10,
            "totalCheckedIn": 4,
            "totalSalesAmount": 100
          },
          {
            "title": "Day2",
            "quota": 10,
            "totalTicketsSold": 5,
            "totalUnitsSold": 5,
            "totalCheckedIn": 1,
            "totalSalesAmount": 50
          }
        ],
        "title": "Week1",
        "quota": 20,
        "totalTicketsSold": 15,
        "totalUnitsSold": 15,
        "totalSalesAmount": 150
      },
      {
        "contents": [
          {
            "title": "Day1",
            "quota": 20,
            "totalTicketsSold": 20,
            "totalUnitsSold": 20,
            "totalCheckedIn": 20,
            "totalSalesAmount": 200
          },
          {
            "title": "Day2",
            "quota": 20,
            "totalTicketsSold": 5,
            "totalUnitsSold": 5,
            "totalCheckedIn": 5,
            "totalSalesAmount": 50
          }
        ],
        "title": "Week2",
        "quota": 40,
        "totalTicketsSold": 25,
        "totalUnitsSold": 25,
        "totalSalesAmount": 250
      }
    ],
    "title": "January",
    "quota": 60,
    "totalTicketsSold": 40,
    "totalUnitsSold": 40,
    "totalSalesAmount": 400
  }
],
"title": "Daily Report",
"quota": 60,
"totalTicketsSold": 40,
"totalUnitsSold": 40,
"totalSalesAmount": 400
  }
}

使用这种方法,我能够消除执行聚合的开销,并且仍然能够使用具有相同签名的集合或单个项目。

希望有人觉得这有帮助!

【讨论】:

    猜你喜欢
    • 2010-10-16
    • 1970-01-01
    • 1970-01-01
    • 2014-09-05
    • 2023-03-20
    • 1970-01-01
    • 2016-07-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多