Scala 为逐步数据存储添加和扩展通用编写器特性的最佳方法

Scala 为逐步数据存储添加和扩展通用编写器特性的最佳方法,scala,design-patterns,generics,dependency-injection,store,Scala,Design Patterns,Generics,Dependency Injection,Store,我需要在我的程序(H2、SQLite、NEO4J、Text等)中存储/写入关于不同和多个数据支持的信息。我逐个创建exporter,因此接口需要易于扩展 我制作了模拟程序,所以我需要在每个步骤中存储信息(需要db对象和要存储的各种数据) 我的第一个想法是将依赖注入模式与泛型方法initializeWriter(),stepWriter(),closeWriter()一起使用,如下所示: trait OutputWriter { Simulation => def path:Stri

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

我制作了模拟程序,所以我需要在每个步骤中存储信息(需要db对象和要存储的各种数据)

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

trait OutputWriter {  Simulation =>

  def path:String

  def initializeWriter()
  def stepWriter()
  def closeWriter()
}
需要实现writer的用户扩展了这个特性,并重写了这个方法。 通常,“database val”包含initializeWriter初始化后的DB对象。 数据对象包含我想要写入的所有可能的数据

我在OutputWriter和Simulation之间有依赖关系,因为实际上我需要从Simulation访问对象和函数来编写最终结果。我认为这不是一个很好的解决方案,并且给出了@x3ro的答案,这是一个更好的解决方案,可以删除这些依赖关系,为编写器提供更好的通用数据对象

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

因此,在这一点上,我的问题是:

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

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

3-代码的一个特定部分,它构成(1)->(2)之间的关系。将一个双精度的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()

}
还有其他的模式(在Literature中或根据您的经验)您经常使用,哪种更健壮或更灵活

非常感谢您的经验回归,
SR.

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

如果你想了解更多,我建议你阅读


可能的问题 对于您的示例,问题在于您的模拟依赖于
DBWriter
,您需要更改实现以使用另一个writer。模拟应仅取决于
OutputWriter
特性

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

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


我的方法 为了改善您的依赖情况,我将进行以下更改

使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)
}
实例化模拟时将写入程序传递给模拟

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


解决编辑中的问题 1-我想在模拟的每一步编写一个数据。此数据的结构相同,只有结果值移动。例如,模拟步骤返回相同的indexedSeq[Double],该indexedSeq[Double]包含在每个步骤的输出上写入的不同值

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

3-代码的一个特定部分,它构成(1)->(2)之间的关系。将一个双精度的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>) = { ... }
}
class SimulationSQLWriter(数据库:DB,表:字符串)扩展OutputWriter{
def初始化(){…}
def stepWriter(数据写入:数据){
val preparedData=prepareDataForInsertion(dataToWrite)
插入数据库(preparedData)
}
def closeWriter(){…}
def prepareDataForInsertion(数据写入:数据)={