如何使调用代码与幕后传递的隐式参数无关?

How to make calling code agnostic of implicit parameter passed under the hood?

我希望能够用交易包围一段代码。调用代码应该像这样简单:

transactional {
  save("something")
}

我想像这样制作 transactional 函数:

def transactional(block: => Unit): Unit = {
  implicit val conn: Connection = ???

  conn.begin()
  try {
    block
    conn.commit()
  } catch {
    case ex: Exception =>
      conn.rollback()
      throw ex
  } finally {
    conn.close()
  }
}

现在 save 方法将需要对 Connection 做一些事情,但我想让调用代码对此不可知(见上文)。我天真地实现了它:

def save(operation: String)(implicit conn: Connection): Unit = {
  println(s"saving $operation using $conn")
}

当然我得到一个编译错误,找不到连接。我缺少什么部分来将连接从 transactional 函数连接到 save 函数?

更改您的 transactional 函数,如下所示(查看交易代码片段)。这里的问题是事务连接可用,但它必须隐式地达到保存功能。因此,一旦您获得连接对象,就将其交给在事务内部运行的函数,然后此代码 (f) 就可以访问该连接。一旦 f 可以访问连接,我们就可以使用 implicit 关键字使它成为 implicit 。现在可以在事务内部无缝调用像保存这样隐式连接的函数。

交易的重要变化

1) 不传递代码块 (block: => Unit) 而是传递 f (f: Connection => Unit)

2) 内部事务将 f 应用于连接对象并授予 f 访问连接对象的权限。

def transactional(f: Connection => Unit): Unit = {
  val conn = getConnectionFromDatabase()
  conn.begin()
  try {
    f(conn)
    conn.commit()
  } catch {
    case ex: Exception =>
      conn.rollback()
      throw ex
  } finally {
    conn.close()
  }
}

现在你可以这样使用了

transactional { implicit conn =>
  save("something")
}

如果你的保存功能是这样的

def save(str: String): Connection => Unit = ???

那你可以不连线

transactional(save("foo"))

如果 save returns 函数需要一个连接:

def transactional(block: Connection => Unit): Unit = {
  val conn: Connection = ???

  // stuff..
  block(conn)
  // stuff..
}

def save(operation: String): Connection => Unit = { conn =>
  println(s"saving $operation using $conn")
}

transactional {
  save("something")
}