分层数据 - 复合模式C#

时间:2017-03-14 20:14:15

标签: c# design-patterns

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

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

所以报告会是这样的:

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});

我是否仍然可以灵活地添加单个订单项或一系列商品,还可以使用合并自动计算/汇总数据?

任何帮助都会很棒!

谢谢你, 阿努普

2 个答案:

答案 0 :(得分:0)

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

答案 1 :(得分:0)

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

我构建了一个界面,如下所示:

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
  }
}

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

希望有人觉得这很有用!