2

我需要在我的程序(H2、SQLite、NEO4J、Text 等)中存储/写入有关不同和多种数据支持的信息。我一一创建导出器,因此接口需要易于扩展。

我制作模拟程序,所以我需要在每一步存储信息(需要 db 对象,以及要存储的各种数据)。

我的第一个想法是使用具有通用方法的依赖注入模式,initializeWriter()如下所示:stepWriter()closeWriter()

trait OutputWriter {  Simulation =>

  def path:String

  def initializeWriter()
  def stepWriter()
  def closeWriter()
}

需要实现 writer 的用户扩展了这个特性,并覆盖了方法。'database val' 通常包含由 initializeWriter 初始化后的 DB 对象。数据对象包含我想写的所有可能的数据。

我在 OutputWriter 和 Simulation 之间存在依赖关系,因为实际上我需要从模拟访问对象和函数来编写最终结果。我认为这不是一个非常好的解决方案,并且鉴于@x3ro 的答案,这是一个更好的解决方案,可以消除这些依赖关系,从而为作者提供一些更好的通用 DATA 对象。

我的问题是在这种情况下,如果我需要添加一些复杂的信息以将此 DATA 正确写入数据库,我可以将包含例如 sqlquery 或其他特定编写器命令的特定代码放在哪里?

所以,此时我的问题是:

1 - 我创建了一个 Data 对象,其中包含要在模拟的每个步骤中写入的数据。这个数据对象的结构是一样的,只是结果值移动了。

2 - 任何输出或多输出写入器,其中包含正确写入输出的所有方法:TextWriter、SQLITEWriter 等。

3 - 使 (1) -> (2) 之间的关系的代码的特定部分。将double的indexedSeq写入文本文件没有问题,但是写入SQL数据库,我需要给表的写入器结构,查询以插入数据等。

我的第一个想法是将此代码设置到 Writer 方法的 stepWriter() 中,但也许有更好的解决方案,因为我认为在这里我打破了 writer 的通用性

object DB
object MyData

trait DBWriter extends OutputWriter {

 val database:DB = DB

 def initializeWriter() = { ... }
 def stepWriter(dataToWrite:MyData) = { ... }

 } 

之后,如果我需要导出我的模拟,我会添加这样的好作者:

new Simulation (...) with DBWriter {
  override def path = "my-path-for-db"

  initializeWriter()

  // computation loop 
 (0 until 50){ var result:MyData= computeData() ; stepWriter(result) }

  closeWriter()

}

还有其他模式(在文献中或根据您的经验)您经常使用更强大或更灵活的模式来做到这一点?

非常感谢您的经验回报,SR。

4

1 回答 1

2

我认为您在示例中介绍的内容不属于“依赖注入”(DI)类别。这是因为 DI 旨在减少代码库中的依赖关系,并且您的类/特征实际上都相互依赖:

如果你想了解更多关于依赖注入的信息,我建议你阅读这篇关于依赖注入的文章。


可能的问题

至于您的示例,问题在于您的 Simulation 依赖于DBWriter,并且您需要更改实现才能使用其他编写器。模拟应该只取决于OutputWriter特征。

另一个问题似乎是stepWriter()您的DBWriter实现方法需要特定于数据库编写器的参数,因此不是通用的(并且根本不符合特征OutputWriter)。

第三个问题是你的OutputWriter特质实际上取决于模拟,我真的找不到原因。为了尽可能保持OutputWriter通用和可重用,你不应该让它依赖于Simulation. 您添加此依赖项的原因是什么?


我的方法

我将进行以下更改以增强您的依赖情况。

使 OutputWriter 和 DBWriter 通用:您OutputWriter实际上应该只是一个接口,如下所示:

trait OutputWriter {
    def initializeWriter()
    def stepWriter(dataToWrite:Data)
    def closeWriter()
}

使 DBWriter 成为一个实际的类

class DBWriter(database:DB) extends OutputWriter {
    def initializeWriter() { ... }
    def stepWriter(dataToWrite:Data) { ... }
    def closeWriter() { ... }
}

实例化时将编写器传递给模拟

object Foo extends App {
    val database:DB = ...
    val writer:OutputWriter = new DBWriter(database)

    new Simulation(..., writer)
}

像这样,您将能够简单地更改传递给Simulation构造函数的编写器,甚至可以同时使用多个编写器!这也允许编写器通过外部配置文件进行配置(请参阅上面提到的依赖注入文章)。


解决您编辑中的问题

1 - 我想编写模拟的每个步骤的数据。这个数据的结构是一样的,只是结果值移动了。例如,模拟步骤返回相同的 indexedSeq[Double],其中包含不同的值以在每一步写入输出。

2 - 任何输出或多输出写入器,其中包含正确写入输出的所有方法:TextWriter、SQLITEWriter 等。

3 - 使 (1) -> (2) 之间的关系的代码的特定部分。将double的indexedSeq写入文本文件没有问题,但是写入SQL数据库,我需要给表的写入器结构,查询以插入数据等。

将 SQL 逻辑放在您的 DBWriter 中是完全可以的(然后我会调用它SimulationSQLWriter)。这当然会使实际的实现不那么通用,但这一定不是一件坏事。始终使您的代码尽可能通用,但尽可能具体,这样您就不必增加很多复杂性来使代码通用,而您根本不需要通用。

始终保持合理的时间、精力和收益比例!

现在看一个具体的例子SimulationSQLWriter

class SimulationSQLWriter(database:DB, table:String) extends OutputWriter {
    def initialize() { ... }
    def stepWriter(dataToWrite:Data) {
        val preparedData = prepareDataForInsertion(dataToWrite)
        insertIntoDatabase(preparedData)
    }
    def closeWriter() { ... }

    def prepareDataForInsertion(dataToWrite:Data) = { ... }
    def insertIntoDatabase(preparedData:<whatever type you need>) = { ... }
}

prepareDataForInsertion可以准备给定的数据并将其放入,例如,将 SQL 字段名称映射到值的映射,或此类映射元素的列表。然后可以将该结果放入insertIntoDatabase中,根据传递给构造函数的参数将其插入到database表中table

于 2012-07-28T12:47:05.840 回答