0

我有三种可能的设计,我正在尝试确定哪一种更好,在哪些情况下它们会更好。所有设计的总体思路是有一个存储一些数据的数据对象和一个分析数据的分析器对象。

我需要在这些设计之间做出选择,我最喜欢 Design 2。但是,与我一起工作的开发人员正在推动设计 3,我认为这是最糟糕的。对于任何设计,我是否缺少任何优点或缺点?让我知道是否需要澄清。

设计一

设计一

在设计 1 中,DataAnalyzer 有一个在构造期间提供的 Data 对象。当客户来电时dataAnalyzer.analyze(),数据被分析。在这个设计中,每个对象的职责是明确的;Data 对象只保存数据,DataAnalyzer 对象分析数据。更改存储的数据只会更改 Data 类,添加分析方法类型只会更改 DataAnalyzer 类。这种设计的一个问题是DataAnalyzer对象只能用于构建时传入的Data对象,所以如果数据对象很多,就需要创建很多DataAnalyzers。另一个缺点(从设计 3 会更清楚)是客户需要知道两个类,而不仅仅是一个。如果有更多类与 Data 关联,则客户端将必须使用所有这些类。

设计二

设计二

设计 2 与设计 1 非常相似,只是现在 DataAnalyzer 是可重用的(用于不同的数据对象)。客户端仍然需要使用两个类,而不是设计 3 中的一个类。职责仍然非常明确,维护也很容易。

设计3

设计3

设计 3 允许客户使用一个对象。客户可以说data.analyze()但不知道有关 DataAnalyzer 的任何信息。我不确定这是否违反单一责任规则;Data 对象有一个允许分析的接口,但责任实际上委托给了 DataAnalyzer。另一个问题是,无论数据是否需要分析,都会为每个创建的 Data 对象创建一个 DataAnalyzer。现在,如果添加更多功能,很多事情都会改变。如果创建了 DataPrinter 类(假设这比让数据打印数据本身更好),客户端不必担心创建 dataPrinter 对象并调用dataPrinter.printData(),它可以调用data.print(). 但是,通过添加这个类,我不得不改变 Data 的接口。向任何 DataXX 类添加更多方法会导致将方法添加到 Data 类。

4

4 回答 4

2

如果分析需要很长时间或大量资源,则设计 1很有用,并且您可以使用某种缓存来使对相同数据的顺序分析运行得更快(或使用更少的资源)。
- 如果您需要存储中间结果或缓存每个正在分析的数据实例的最终结果,请使用此选项。

如果您想要一个静态的、线程安全的分析器,该分析器可以为不同的数据反复调用,并且不需要资源来执行此操作,或者可以从资源池中为每个请求获取不同的资源,那么设计 2很有用。

如果您有各种数据类型(具有相同的基类或接口),您可以使用访问者模式、依赖注入、反射或命令模式从主类到达正确的具体数据分析器类,因此不会违反单一职责原则。

设计 3是一个坏主意,因为这意味着您将数据类型与对其进行的处理相结合 - 每次您想要添加或更改处理时,如果您决定使用此设计,您将诅咒您的决定 :-)

在某些语言中,例如在 C# 3.0+ 中,有一种糖语法可以使设计 2 的操作使用设计 3 的语法工作。

于 2012-06-26T16:13:06.380 回答
1

快速回答

从面向对象的角度很容易想到,Design 3可能会更好,因为“数据”是一个对象,而“分析”似乎不仅仅是一个操作。

但是,在实践中,正如@Danny Varod 所说:数据的“糟糕耦合”。

在现实生活中,分析器变化很小,而数据变化很大,从面向对象的角度来看,分析器会改变数据,因此设计 1似乎更适合您的情况。

评论

我在业余时间使用编译器和解析器以及他们分析的编程语言。这种情况看起来与您的问题相似。解析器看起来像您的“数据分析器”,要解析的源代码看起来像您的“数据”。

关系示例:

................................
....+----------------------+....
....|      CPPParser       |....
....+----------------------+....
....| [+] CPPStream Source |....
....+----------+-----------+....
...............|................
...............|................
...............v................
....+----------+-----------+....
....|     <<abstract>>     |....
....|       CPPStream      |....
....+----------------------+....
................................

但是,由于我的“数据提供者”,在这种情况下,流可以被后代类替换,例如 FileStream、StringStream。

继承图示例:

............................................................
....+----------------------+................................
....|      CPPStream       |................................
....+----------------------+................................
....| [+] CPPStream Source |................................
....+----------+-----------+................................
...............^............................................
...............|............................................
...............|............................................
...............+---------------------------+................
...............|...........................|................
...............|...........................|................
...............|...........................|................
....+----------+-----------+....+----------+-------------+....
....|     <<concrete>>     |....|      <<concrete>>      |....
....|    CPPFileStream     |....|     CPPFileStream      |....
....+----------------------+....+------------------------+....
....| [+] String Filename  |....| [+] String StringValue |....
....+----------------------+....+------------------------+....
............................................................

怎么样,你的场景,由一个类表示的数据可以被一个后代类替换吗?

如果是这样,设计 1似乎更适合。

干杯。

于 2012-06-26T20:56:27.637 回答
0

看看你的担忧,它看起来像分析是一种静态方法。因此,您可以拥有一个 Utility 类,该类将分析作为接受数据作为输入的静态方法。dataPrinting 可以是同一实用程序类中的另一个静态方法。如果有多种方法(算法)来分析数据,这种设计就会失败。如果这是必需的,那么策略模式可以帮助您。

于 2012-06-26T18:40:18.180 回答
0

设计 3 不是很理想,因为数据分析器必须始终存在。我敢肯定,在很多情况下,您并不总是需要将该类附加到 DO。另外,我同意它违反了单一责任原则。

如果不同数据对象类型存在不同的内部机制/方法,则设计 2 可能会出现一些问题。您每次都必须重新初始化它。我认为这也违反了单一职责原则。数据分析器应该只需要担心一种类型的数据对象。每次有新数据对象进入时,它不应该重置和重做其内部工作。

设计 1 是三者中我最喜欢的,但不是我理想的设计。理想情况下,我会将数据对象传递给分析器工厂。工厂为该特定数据对象生成一个分析器,然后分析器可以完成它的工作。这可能是最可维护的解决方案。

于 2012-06-26T15:55:19.967 回答