scala - 添加和扩展通用编写器特征以进行逐步数据存储的最佳方法

标签 scala design-patterns generics dependency-injection store

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

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

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

trait OutputWriter {  Simulation =>

  def path:String

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

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

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

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

所以,此时我的问题是:

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

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。

最佳答案

我认为您在示例中呈现的内容不属于“依赖注入(inject)”(DI)类别。这是因为 DI 的目的是减少代码库中的依赖性,而您的类/特征实际上都是相互依赖的:

我建议您阅读this article on Dependency Injection如果您想了解更多信息。


可能的问题

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

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

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


我的方法

我将进行以下更改以改善您的依赖性情况。

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

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 的 writer。构造函数,甚至同时使用多个编写器!这还允许编写器通过外部配置文件进行配置(请参阅上面提到的有关依赖项注入(inject)的文章)。


解决编辑中的问题

1 - Data i want to write a each step of my simulation. Structure of this data is the same, only the result value move. For example the simulation step return the same indexedSeq[Double] which contain different values to write on output at each step.

2 - Any output or multiple output writer, which contain all the method to write correctly on the output : TextWriter, SQLITEWriter, etc.

3 - A specific part of code which make the relation between (1) -> (2). There is no problem to write an indexedSeq of double into text file, but into an SQL Database, i need to give to the writer structure of table, query to insert data, etc.

将 SQL 逻辑放入 DBWriter 中是完全可以的(然后我将其称为 SimulationSQLWriter )。这当然使得实际的实现不那么通用,但这一定不是一件坏事。始终使您的代码尽可能通用,但根据需要尽可能具体,这样您就不必添加大量复杂性来使代码通用,而您根本不需要通用。

始终在时间、精力和 yield 之间保持合理的比例!

现在来看一个 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 .

关于scala - 添加和扩展通用编写器特征以进行逐步数据存储的最佳方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11691131/

相关文章:

scala - SBT 项目刷新失败 [IntelliJ、Scala、SBT]

java - 如何定义要从生产中的不同子目录提供服务的 Play Assets 和 API?

mysql - 使用 Slick (Scala) 将记录插入数据库,实体的最佳实践

java - 反序列化时如何正确使用 transient 关键字?

java - Autowiring 在 Spring 4 中不起作用

带有类型检查的Scala相等性?

c# - 报告/监控长流程进度的设计模式

java - 在 java/groovy 中拆分出非常大的枚举

java - 创建指定参数类型或其子类型的 Arraylist

java - Java 线程中的泛型