我有三种可能的设计,我试图确定哪一种更好,哪种情况下会更好。所有设计的一般概念是存在一个存储一些数据的数据对象,并且有一个分析数据的分析器对象。
我需要在这些设计之间做出选择,我最喜欢Design 2。但是,我正在与之合作的开发人员正在推动Design 3,我认为这是最糟糕的。对于任何设计,我都缺少任何优点或缺点吗?如果需要澄清,请告诉我。
在设计1中,DataAnalyzer具有在构造期间提供的Data对象。当客户端调用dataAnalyzer.analyze()
时,将分析数据。在这个设计中,每个对象的责任都是明确的; Data对象只保存数据,DataAnalyzer对象分析数据。更改存储的数据只会更改Data类,添加分析方法类型只会更改DataAnalyzer类。此设计的一个问题是DataAnalyzer对象只能用于构造期间传入的Data对象,因此如果有大量数据对象,则需要创建大量DataAnalyzers。另一个缺点(设计3中更清楚)是客户端需要知道两个类而不是一个类。如果有更多的类与Data有关联,那么客户端必须在所有这些类中工作。
设计2与设计1非常相似,但现在DataAnalyzer是可重用的(对于不同的数据对象)。客户仍然需要使用两个类,而不是设计3中的一个。责任仍然非常明确,维护也很容易。
设计3允许客户端使用一个对象。客户端可以说data.analyze()
并且对DataAnalyzer一无所知。我不确定这是否违反了单一责任规则; Data对象有一个允许分析的接口,但责任实际上委托给了DataAnalyzer。另一个问题是,无论是否需要分析数据,都会为创建的每个Data对象创建DataAnalyzer。现在,如果添加更多功能,很多事情都会发生变化。如果创建了一个DataPrinter类(让我们假设这比数据打印数据本身更好),客户端就不必担心创建一个dataPrinter对象并调用dataPrinter.printData()
,它只能调用{{1} }}。但是,通过添加这个类,我不得不改变Data的接口。向任何DataXX类添加更多方法会导致将方法添加到Data类。
答案 0 :(得分:2)
设计1 非常有用,如果分析需要很长时间或大量资源,并且您可以使用某种缓存来对相同数据进行顺序分析(或使用更少的资源) 。
- 如果您需要存储中间结果或缓存您正在分析的每个数据实例的最终结果,请使用此选项。
设计2 非常有用,如果你想要一个静态的,线程安全的分析器,它可以反复调用不同的数据,并且不需要资源来完成它,或者可以从每个请求获得不同的资源。资源池。
如果您有各种数据类型(具有相同的基类或接口),您可以使用访问者模式,依赖项注入,反射或命令模式从主要数据类型到达正确的具体数据分析器类,从而不违反单一责任原则。
设计3 是一个坏主意,因为这意味着您将数据类型与在其上完成的处理相结合 - 每次要添加或更改处理时,如果您要诅咒您的决定决定使用这种设计: - )
在某些语言中,例如在C#3.0+中,有一个sugar syntax,可以使用设计3的语法使Design 2的操作工作。
答案 1 :(得分:1)
快速回答
从面向对象的角度来看很容易想到,设计3 可能更好,因为“数据”是一个对象,“分析”似乎不仅仅是一个操作。
但是,在实践中,正如@Danny Varod所说:“数据的”错误耦合“。
在现实生活中,分析仪变得很小,&数据变化很多,&从面向对象的角度来看,分析器将更改数据,因此设计1 似乎更适合您的情况。
<强>注释
我在业余时间与Compilers&amp;解析器,以及他们分析的编程语言。这种情况与您的问题类似。 Parser看起来像是一个“数据分析器”,要解析的源代码看起来就像是“数据”。
关系示例:
................................
....+----------------------+....
....| CPPParser |....
....+----------------------+....
....| [+] CPPStream Source |....
....+----------+-----------+....
...............|................
...............|................
...............v................
....+----------+-----------+....
....| <<abstract>> |....
....| CPPStream |....
....+----------------------+....
................................
但是,因为我的“数据提供者”,在本例中是一个流,可以被后代类替换,例如FileStream,StringStream。
继承图示例:
............................................................
....+----------------------+................................
....| CPPStream |................................
....+----------------------+................................
....| [+] CPPStream Source |................................
....+----------+-----------+................................
...............^............................................
...............|............................................
...............|............................................
...............+---------------------------+................
...............|...........................|................
...............|...........................|................
...............|...........................|................
....+----------+-----------+....+----------+-------------+....
....| <<concrete>> |....| <<concrete>> |....
....| CPPFileStream |....| CPPFileStream |....
....+----------------------+....+------------------------+....
....| [+] String Filename |....| [+] String StringValue |....
....+----------------------+....+------------------------+....
............................................................
您的方案怎么样,由类代表的数据能否被后代类替换?
如果是这种情况,设计1 似乎更适合。
干杯。
答案 2 :(得分:0)
设计3不是很理想,因为数据分析器必须始终存在。我确信在很多情况下你并不总是需要将这个类附加到DO上。此外,我同意它违反了Single Responsiblity委托人。
如果对于不同的数据对象类型存在不同的内部机制/方法,则设计2可能会出现一些问题。您每次都必须重新初始化它。我认为这也违反了单一责任委托人。数据分析器只需要担心一种类型的数据对象。每次有新数据对象进入时,都不必重置和重做其内部工作。
设计1是我最喜欢的三个,但不是我理想的设计。理想情况下,我会将数据对象传递给Analyzer工厂。工厂为该特定数据对象吐出分析器,然后分析仪可以完成其工作。这可能是最易维护的解决方案。
答案 3 :(得分:0)
看看你的担忧,看起来分析是一种静态的方法。因此,您可以拥有一个Utility类,它将analyze作为静态方法接受数据作为输入。 dataPrinting可以是同一实用程序类中的另一个静态方法。如果有多种方法(算法)来分析数据,则此设计将失败。如果需要,那么策略模式可以帮助你。