Scala 为逐步数据存储添加和扩展通用编写器特性的最佳方法
我需要在我的程序(H2、SQLite、NEO4J、Text等)中存储/写入关于不同和多个数据支持的信息。我逐个创建exporter,因此接口需要易于扩展 我制作了模拟程序,所以我需要在每个步骤中存储信息(需要db对象和要存储的各种数据) 我的第一个想法是将依赖注入模式与泛型方法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
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()
方法需要特定于数据库编写器的参数,因此不是通用的(并且根本不符合traitOutputWriter
)
第三个问题是,您的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(数据写入:数据)={