2

この時点で、設計に関する大きな問題に直面しています。私の方法は、次のことを達成しようとしています:

  1. 渡されたオブジェクトをデータベースに挿入します。
  2. 挿入から自動インクリメントされた ID を取得し、それを使用してオブジェクトと共に webservice1 を呼び出します。
  3. webservice1 から結果を取得し、元のオブジェクトと webservice1 からの応答を使用して webservice2 を呼び出します。
  4. webservice1 と 2 の結果を組み合わせて、データベースに書き込みます。
  5. 最後の挿入から結果の自動インクリメントされた ID を取得し、最終的に操作の成功または失敗につながる元のオブジェクトで webservice3 を呼び出します。

要件は流動的であり、変更に基づいてロジックを変更し続けたくないため、これを柔軟な方法で設計したいと考えています。ある程度の変更は避けられないことは承知していますが、被害を最小限に抑え、オープンクローズの原則を尊重したいと考えています。

私の最初の見解は次のとおりです。

def complexOperation(someObject:T) = 
   dbService.insertIntoDb(someObject) match {
     case Left(e:Exception) => Left(e)
     case Right(id:Int) => webService.callWebService1(id,someObject) match {
        case Left(e:Exception) => Left(e)
        case Right(r:SomeResponse1) => webService.callWebservice2(r,someObject) match {
          case Left(e:Exception) => webService.rollbackService1();Left(e)
          case Right(context:ResponseContext) => dbService.insertContextIntoDb(context) match {
            case Left(e:Exception) => Left(e)
            case Right(id:Int) => webService.callWebservice3(id,someObject) match {
               case Left(e:Exception) => webService.rollbackService3();Left(e)
               case Right(r:Response) => Right(r)
            }
          }
        } 
     }

ご覧のとおり、これはもつれた混乱です。制御不能になった場合、単体テストも拡張も簡単にデバッグすることもできません。このコードはその目的を果たしますが、コードを継承する人々の生活をもう少し楽にするためにコードをリファクタリングする方法についていくつかのアイデアを得ることは素晴らしいことです.

ありがとう

4

3 に答える 3

2

理解のために使用して、コード内のノイズを減らすことができます。

于 2012-10-28T01:34:43.590 に答える
2

scala.util.Tryを見てください。これは Scala 2.10 で利用可能であり、オプションとして利用できる場合とできない場合がありますが、そのアイデアはシナリオに最適です。

コード例にあるのは、ネストの「ピラミッド」と呼ぶのが好きなものです。これに対する最善の解決策は、可能な限りフラット マッピングを使用することです。Either[Exception, Result]しかし、明らかに、すべてのステップでのようなものがある場合、それは問題です。は基本的に の代わりであり、必要なすべての機能が備わっていますTryTry[T]Either[Exception, T]flatMap

これらの呼び出しの戻り値の型を変更するか、 からへwebServiceの暗黙的な変換を提供できると仮定すると、コード ブロックは次のようになります...Either[Exception, Result]Try[Result]

for {
    id <- dbService.insertIntoDb(someObject)
    r <- webService.callWebService1(id,someObject)
    context <- webService.callWebservice2(r,someObject)
    id2 <- dbService.insertContextIntoDb(context)
    response <- webService.callWebservice3(id,someObject).recoverWith {
        case e: Exception => webService.rollbackService3(); Failure(e)
    }
} yield response

Lift はnet.liftweb.common.Boxに同様のメカニズムを持っています。に似Optionていますが、例外用のコンテナもあります。

編集:leftのorrightメソッドを使用できるようで、で説明したのとほぼ同じ方法で -ingEitherを使用できます。唯一の違いは、最終結果がではなく になることです。詳細/例については、LeftProjectionを確認してください。flatMapTryEither[Exception, Result]Try[Result]

于 2012-10-28T01:46:20.790 に答える
2

@Dylanは上記の正しい考えを持っていました。あなたがやりたいことを慣用的な Scala 2.9.1 コードに翻訳するのを手伝うことができるかどうか見てみましょう。

このバージョンはロールバックを試みません:

// 1: No rollbacks, just returns the first exception in Left
def complexOperation1(someObject:T): Either[Exception, Response] = {      
  for {
    id       <- dbService.insertIntoDb(someObject).right
    r        <- webService.callWebService1(id, someObject).right
    context  <- webService.callWebservice2(idResp, someObject).right
    id2      <- dbService.insertContextIntoDb(context).right
    response <- webService.callWebservice3(id,someObject).right
  } yield response 
}

それでは、上記とまったく同じようにロールバックを実行してみましょう。

// 2: Rolls back all web services and returns first exception in Left       
def complexOperation1(someObject:T): Either[Exception, Response] = {      
  for {
    id       <- dbService.insertIntoDb(someObject).right
    r        <- webService.callWebService1(id, someObject).right
    context  <- webService.callWebservice2(idResp, someObject).left.map { e =>
                  webService.rollbackService1()
                  e
                }.right
    id2      <- dbService.insertContextIntoDb(context).right
    response <- webService.callWebservice3(id,someObject).left.map { e =>
                  webService.rollbackService3()
                  e
                }.right
  } yield response 
}

左側の効果 (ロールバック) を行う関数を定義すると、少しすっきりしてテストしやすくなります。次に例を示します。

// 3: Factor out the side-effect of doing the follbacks on Left
def rollbackIfLeft[T](f: => Either[Exception, T], r: => Unit): Either[Exception, T] = {
  val result = f
  result.left.foreach(_ => r) // do the rollback if any exception occured
  result
}

def complexOperation1(someObject:T): Either[Exception, Response] = {      
  for {
    id       <- dbService.insertIntoDb(someObject).right
    r        <- webService.callWebService1(id, someObject).right
    context  <- rollbackIfLeft(webService.callWebservice2(idResp, someObject),
                               webService.rollbackService1()).right
    id2      <- dbService.insertContextIntoDb(context).right
    response <- rollbackIfLeft(webService.callWebservice3(id,someObject),
                               webService.rollbackService3()).right
  } yield response 
}

scala REPL で試してrollbackIfLeft、感覚をつかむことができます。

scala> rollbackIfLeft(Right(42), println("hey"))
res28: Either[Exception,Int] = Right(42)

scala> rollbackIfLeft(Left(new RuntimeException), println("ERROR!"))
ERROR!
res29: Either[Exception,Nothing] = Left(java.lang.RuntimeException)

お役に立てれば!

于 2013-02-06T22:31:31.357 に答える