【问题标题】:Oop data structure adviceoop 数据结构建议
【发布时间】:2011-07-20 14:22:26
【问题描述】:

我正在编写一个日志文件解码器,它应该能够读取许多不同的文件结构。我的问题是如何最好地表示这些数据。我正在使用 C#,但对 OOP 不熟悉。

一个例子: 日志文件具有一系列传感器值。一个传感器读数可以称为 A,另一个称为 B。显然,有不止 2 种条目类型。 在不同的日志文件中,它们可以存储为 ABABABABAB 或 AAAAABBBBB。

我正在考虑将其描述为条目块。所以在第一种情况下,一个块将是“AB”,有 5 个块。在第二种情况下,第一个块是'A',读取5次。这后面是一个'B'块,读取5次。

这是相当简单的(实际上有 40 种不同类型的日志文件,每个块中最多包含 40 个传感器值)。没有日志有超过 300 个块。

目前,我将所有这些都存储在数据表中。每个条目都有一个列,具有要阅读的数量的属性。如果设置为 -1,它将继续到块中的下一列。如果没有,它将假定它已经到达了块的末尾。

这一切看起来都很笨拙。任何人都可以提出更好的方法吗?

【问题讨论】:

  • 是否应该便于用户阅读?
  • 日志文件为二进制格式。我需要阅读它并在 GUI 中显示它。它需要在 GUI 中清晰,所以到目前为止我已经尝试使用 DataGridView。

标签: c# oop data-structures ooad


【解决方案1】:

我认为您应该先从here, 开始,然后再从here 开始了解什么是面向对象编程。在学习 OOP 时不要担心您当前的问题。

在学习 OO 概念时,您应该开始了解代码不是数据,数据不是代码。从 OOP 立场如何表示数据并不重要。您可以编写 OO 代码来消费您的数据,也可以编写程序代码来消费您的数据,这部分与数据的格式无关。

那么回到你的问题

我的问题是如何最好地表示这些数据

这取决于您的需求。什么是写日志文件?你能控制作者和读者吗?如果我这样做了,我将依靠构建内置的serialization 方法来最小化我需要编写的代码量。日志文件真的会很长吗?如果是这样,您描述的“数据表”方法通常会更好。如果日志文件的文件大小不会很大,那么 XML 真的很容易使用。

【讨论】:

  • 嗨,鲍勃,感谢您提供的链接。我已经尝试阅读了很多关于这个主题的内容 - 但这只是让我的头脑进入我正在努力的正确思维方式!我相信这是有经验的,但目前似乎有 100 种方法可以做到这一点,而且我敢肯定还有 100 种我什至还没有想到。日志文件正在其他地方以二进制格式生成。我只需要解码并显示它——但不能改变它的结构。我认为序列化会在原始数据周围添加额外的信息——我在这里不正确吗?我该怎么办?
  • 当您需要使用多种日志文件格式时,为您的问题提供 OO 解决方案的优势就会开始显现。基类将定义如何使用所有常用方法解码文件的过程,例如打开文件、读取 ling、关闭文件、返回显示结果等。当你说日志文件是 generated in binary format elsewhere 时,你会意思是标准意义上的二进制?因为如果是这种情况,二进制解散是您真正拥有的唯一选择。
  • “二进制格式”是指如果条目以日期开头,则前六个字节可能是 0x070b0a023125(2010 年 11 月 7 日,02:49:37)。当我尝试序列化一个类时,它会在实际数据周围增加大量数据 - 您能否举例说明您将如何处理这个问题?
【解决方案2】:

非常基本和直接:

  1. 使用string EntryBlock, int Count 等属性为IEnrty 定义一个接口
  2. 定义一个代表Entry并实现IEntry的类
  3. 进行二进制序列化的代码应该知道接口,例如它应该引用IEnumerable<IEntry>
  4. Entry 类可以覆盖 ToString() 以返回类似 [ABAB-2] 的内容,如果这在序列化过程中会有所帮助,那么肯定会有所帮助
  5. 接口 IEntry 可以提供方法 void CreateFromRawString(string rawDataFromLog) 如果有帮助,请自行决定

如果您想了解更多信息,请分享您用于序列化/反序列化的代码

【讨论】:

  • 感谢您的回复。那么......“Entry”类会描述块并保存读取的数据数组吗?所以另一个'Parser'类可以遍历'Entry'类,检查读取什么类型的变量并将其存储在数组中?
  • 好的.. 那么你会为读取的数据使用列表吗?我想没有类型安全的方法可以做到这一点,因为块可以有 datetime、float 或 int 值?
  • 所以让 IEntry 泛型和属性 T BlockData
  • So.. 一个“Field”类,它定义了哪种类型(int、float、byte)、描述性名称和实际值,然后是一个包含“Field”列表的“block”类' 和要读取的数字,然后是“日志”类,其中包含“块”列表以及日志名称和识别特征等内容?是 IEntry 的“字段”吗?那么,如果所有的 都不同,我怎么能有一个 IEntry 的列表呢?这可能吗?
  • 如果所有字段都实现了相同的 IField 接口,那么 T 可以被限制为 where T: IField
【解决方案3】:

除了 Bob 提供的内容之外,我强烈推荐 Head First Design Patterns 作为 C# 程序员对 OO 的温和但稳健的介绍。示例使用 Java 编写,可轻松转换为 C#。

【讨论】:

  • 谢谢。我目前正在通过它!这是一本很棒的书——尤其是对于像我这样的初学者来说,但我仍然发现很难实现逻辑的飞跃,看看它们如何适用于我的代码。
【解决方案4】:

至于OOP,你想学SOLID

我建议您使用测试驱动开发来构建它。

从小处着手,使用日志数据的一个简单片段并编写类似的测试(您会根据经验找到更好的方法并将其应用于您的情况):

[Test]
public void ReadSequence_FiveA_ReturnsProperList()
{
  // Arrange
    string sequenceStub = "AAAAA";

    // Act
    MyFileDecoder decoder = new MyFileDecoder();
    List<string> results = decoder.ReadSequence(sequenceStub);

    // Assert
    Assert.AreEqual(5, results.Count);
    Assert.AreEqual("A", results[0]);
}

该测试代码 sn-p 只是一个起点,我试图在断言中变得相当冗长。随着时间的推移,你可以想出更多创造性的方法。关键是从小处着手。一旦此测试通过,请添加另一个测试,在其中混合“AB”并更改您的解码器以正确处理此问题。最终,您将拥有处理不同格式的大量测试。使用 TDD,您将走上正确使用 SOLID 的道路。每当你发现一些你无法测试的东西时,你应该检查规则,看看你是否不能让它更简单并注入依赖项。

最终你会开始嘲笑。例如,您可能会发现您宁愿为您的MyFileDecoder 类注入能够读取您的日志文件的依赖项。在这种情况下,您将创建一个模拟对象并将其传递给构造函数,并将模拟设置为在调用方法时返回 sequenceStub

【讨论】:

  • 谢谢。我最近阅读了很多关于 C#/OOP/etc 的内容。但之前没有遇到过 SOLID。我会好好读一读!您对如何处理数据有什么建议吗?
猜你喜欢
  • 1970-01-01
  • 2016-05-21
  • 2013-03-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-04-21
  • 2013-09-26
  • 1970-01-01
相关资源
最近更新 更多