3

私は、Some/None/Option の背後にある原則と概念全体を理解しており、そのメリットを確かに理解できます。私の質問は、よりベストプラクティスです。いつそれが過剰になり、いつそれを宗教的に使用することが理にかなっていますか. 私はそう思います (そして私は間違っている可能性があります) が、(null とは対照的に) 何も渡さない方が安全な方法であるため、可能な限り使用することは理にかなっています。私がよくやっていると思うのは、map、getOrElse、get、match が散らばっているいくつかの関数を持っていることです。私が見逃している概念や、複数の Optional 値を受け取る関数を使用するためのベスト プラクティスはありますか。例えば:

  def updateJobs(id: Int) = withAuth {
    request => {

      User.userWithToken(request.headers.get("token").get).map {
        user =>
          Job.jobsAfterIdForForeman(id.toString, user.id.toString) match {
            case Some(json) => Ok(json)
            case _ => NoContent
          }
      }.getOrElse(BadRequest)
    }
  }

またはさらに悪い例:

  def addPurchaseRequest(json: JsValue) = {
    (json \ "jobId").asOpt[Long].map {
      jobId => JobDAO.jobWithId(jobId).map {
        job => PurchaseRequestDAO.insert(new PurchaseRequest(json, job)).map {
          model =>
            val request = model.asInstanceOf[PurchaseRequest]
            (json \ "items").asOpt[List[JsObject]].map {
              list => {
                if (PurchaseItemAssociationDAO.bulkInsert(PurchaseItemAssociation.itemsFromJsonArray(list, request.id))) Option(request.addResponseJson) else None
              }
            }.getOrElse(None)
        }.getOrElse(None)
      }.getOrElse(None)
    }.getOrElse(None)
  }

クレイジーに見えないようにいくつかをリファクタリングすることができましたが、これをリファクタリングしてそれほどクレイジーに見えないようにするより良い方法はありますか? 私は何かを見逃していますか、それともこのように見えるだけのものに慣れていますか? 確かに、よりクリーンなプラクティスが必要なようです。

4

2 に答える 2

8

Option クラスはモナドであるため、for内包表記を使用してコードをきれいに見せる必要があります。たとえば、2 番目の例は次のように書き直すことができます。

def addPurchaseRequest(json: JsValue) = 
  for {
    jobId <- (json \ "jobId").asOpt[Long]
    job <- JobDAO.jobWithId(jobId)
    model <- PurchaseRequestDAO.insert(new PurchaseRequest(json, job))
    request = model.asInstanceOf[PurchaseRequest]
    list <- (json \ "items").asOpt[List[JsObject]]
      if PurchaseItemAssociationDAO.bulkInsert(PurchaseItemAssociation.itemsFromJsonArray(list, request.id))
  } yield request.addResponseJson
于 2013-10-18T15:18:10.450 に答える
0

for 内包表記を使用するか、少なくともflatMap代わりに使用map / getOrElse(None)して物事をよりコンパクトにします。また、新しい変数を独自の行ではなく、前の行の最後に置くのが通例です。これにより、最悪のケースが大幅にクリーンアップされます。

あるいは、中間ロジックを必要とする特に長いチェーンでは、例外のようなメカニズムが for 内包表記よりもうまく機能することがわかりました (非ローカルの戻り値と同じ原則に基づいています)。

trait Argh extends scala.util.control.ControlThrowable
implicit class GetOrArgh[A](val underlying: Option[A]) extends AnyVal {
  def or(a: Argh) = underlying match {
    case None => throw a
    case _ => underlying.get
  }
}
def winnow[A](f: Argh => A): Option[A] = {
  val argh = new Argh { }
  try { Some(f(argh)) }
  catch { case x: Argh if (x eq argh) => None }
}

次に、次のように使用します。

def addPurchaseRequest(json: JsValue) = winnow { fail =>
  val jobId = (json \ "jobId").asOpt[Long] or fail
  val job = JobDAO.jobWithId(jobId) or fail
  val model = PurchaseRequestDAO.insert(new PurchaseRequest(json, job)) or fail
  val request = model match {
    case pr: PurchaseRequest => pr
    case _ => throw fail
  }
  val list = (json \ "items").asOpt[List[JsObject]]
  if (!PurchaseItemAssociationDAO.bulkInsert(
    PurchaseItemAssociation.itemsFromJsonArray(list, request.id)
  )) throw fail
  request.addResponseJson
}

このアプローチと for 理解のどちらがうまく機能するかは、(私の経験では) 中間処理をどれだけ行う必要があるかによって異なります。すべてをワンライナーにすることができれば、for-comprehension は素晴らしくきれいです。もう少し複雑なものが必要な場合は、このアプローチの方が気に入っています。

内包表記は正規の Scala 構造ですが、これには新しいユーザーの学習が必要になることに注意してください。したがって、人々が迅速に理解する必要があるかもしれないコード内の内包表記または flatMap を支持します。

于 2013-10-19T00:50:02.173 に答える