2

プログラムでさまざまなデータ サポート (H2、SQLite、NEO4J、テキストなど) に関する情報を保存/書き込みする必要があります。エクスポーターを 1 つずつ作成するため、インターフェイスは簡単に拡張できる必要があります。

シミュレーション プログラムを作成するので、各ステップで情報を保存する必要があります (db オブジェクトと、保存するさまざまなデータが必要です)。

私の最初のアイデアは、次のように、ジェネリックメソッドinitializeWriter()で依存性注入パターンを使用することです:stepWriter()closeWriter()

trait OutputWriter {  Simulation =>

  def path:String

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

ライターを実装する必要があるユーザーは、このトレイトを拡張し、メソッドをオーバーライドします。通常、'database val' には、initializeWriter による初期化後の DB オブジェクトが含まれます。データオブジェクトには、書き込みたいすべての可能なデータが含まれています。

実際には、シミュレーションからオブジェクトと関数にアクセスして最終結果を書き込む必要があるため、OutputWriter とシミュレーションの間に依存関係があります。それは本当に良い解決策ではないと思います.@x3roの答えを考えると、これらの依存関係を削除して、より良い汎用DATAオブジェクトをライターに与える方が良い解決策です。

この場合、この DATA をデータベースに正しく書き込むために複雑な情報を追加する必要がある場合、sqlquery やその他の特定のライター コマンドなどを含むこの特定のコードをどこに置くことができますか?

したがって、この時点で私の問題は次のとおりです。

1 - シミュレーションの各ステップで書き込むデータを含むデータ オブジェクトを作成します。このデータ オブジェクトの構造は同じで、結果の値のみが移動します。

2 - 出力に正しく書き込むためのすべてのメソッドを含む任意の出力または複数の出力ライター: TextWriter、SQLITEWriter など。

3 - (1) -> (2) の関係を作るコードの特定の部分。double の indexedSeq をテキスト ファイルに書き込むのは問題ありませんが、SQL データベースに書き込むには、テーブルの構造、データを挿入するためのクエリなどをライターに渡す必要があります。

私の最初のアイデアは、このコードをライターのメソッドの stepWriter() に設定することですが、ここでライターの汎用性を壊していると思うので、おそらくこれにはより良い解決策があります

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 がコードベースの依存関係を減らすことを目的としており、クラス/特性が実際にはすべて相互に依存しているためです。

依存性注入について詳しく知りたい場合は、依存性注入に関するこの記事を読むことをお勧めします。


考えられる問題

あなたの例に関しては、問題はあなたのシミュレーションがに依存していることDBWriterであり、別のライターを使用するには実装を変更する必要があります。OutputWriterシミュレーションは特性のみに依存する必要があります。

もう 1 つの問題はstepWriter()、実装のメソッドがDBWriterデータベース ライターに固有のパラメーターを必要とするため、一般的ではない (そして、トレイトOutputWriterにまったく準拠していない) ことです。

3 つ目の問題は、あなたの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 データベースに書き込むには、テーブルの構造、データを挿入するためのクエリなどをライターに渡す必要があります。

DBWriter に SQL ロジックを配置することはまったく問題ありません (これを呼び出します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に挿入される にスローされる可能性があります。databasetable

于 2012-07-28T12:47:05.840 に答える