加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

scala – 链功能以不同的方式

发布时间:2020-12-16 18:09:00 所属栏目:安全 来源:网络整理
导读:Scala函数具有以下链接方法: fn1.andThen(fn2) fn1.compose(fn2) 但是如何写这个案例: 我有函数cleanUp(),必须始终作为最后一步调用. 我还有很多其他功能,比如: class Helper { private[this] val umsHelper = new UmsHelper() private[this] val user =
Scala函数具有以下链接方法:

fn1.andThen(fn2)
 fn1.compose(fn2)

但是如何写这个案例:

我有函数cleanUp(),必须始终作为最后一步调用.
我还有很多其他功能,比如:

class Helper {
  private[this] val umsHelper = new UmsHelper()
  private[this] val user = umsHelper.createUser()
  def cleanUp = ... // delete user/ and other entities

  def prepareModel(model: TestModel) = {
    // create model on behalf of the user
  }

  def commitModel() = {
    // commit model on behalf of the user
  }
}

一些外部代码可以使用这样的代码:

val help = new Helper()
help.prepareModel()
help.commitModel()
// last step should be called implicitly cleanUp

如何以功能的方式编写,链接总是如此
隐式调用cleanUp函数作为最后一步?

注意:我认为它是C语言中析构函数的类比.有些链接(无论这个链是怎么做的)fn1和后来fn2和后者fn3必须调用最后一步cleanUp(fn1和Later fn2以及后期fn3和后续cleanUp).错误的直接写cleanUp方法有一个很大的变化有人会错过这一步,用户将被泄露(留在数据库中)

解决方法

这是一个更高级的选择:

当你听到“背景”和“步骤”时,会有一种直接浮现在脑海中的功能模式:Monads.汇总您自己的monad实例可以简化将有效步骤放在一起的用户端,同时提供保证,以便在它们之后清理上下文.

在这里,我们将开发一个遵循该模式的“CleanableContext”结构.

我们将构造基于最简单的monad,其唯一的功能是保持一个值.我们打算称之为Context

trait Context[A] { self => 
  def flatMap[B](f:A => Context[B]): Context[B] = f(value)
  def map[B](f:A => B): Context[B] = flatMap(f andThen ((b:B) => Context(b)))
  def value: A
}

object Context {
  def apply[T](x:T): Context[T] = new Context[T] { val value = x  }
}

然后我们有一个CleanableContext,它能够“自我清理”,提供一些’清理’功能:

trait CleanableContext[A] extends Context[A] {
  override def flatMap[B](f:A => Context[B]): Context[B] = {
    val res = super.flatMap(f)
    cleanup
    res
  }
  def cleanup: Unit
}

现在,我们有一个对象能够生成一个可清理的UserContext,它将负责管理用户的创建和销毁.

object UserContext {
  def apply(x:UserManager): CleanableContext[User] = new CleanableContext[User] {
    val value = x.createUser
    def cleanup = x.deleteUser(value)
  }
}

假设我们已经定义了我们的模型和业务功能:

trait Model
trait TestModel extends Model
trait ValidatedModel extends Model
trait OpResult
object Ops {
  def prepareModel(user: User,model: TestModel): Model = new Model {}

  def validateModel(model: Model): ValidatedModel = new ValidatedModel {}

  def commitModel(user: User,vmodel: ValidatedModel): OpResult = new OpResult {}
}

用法

有了这种可重复使用的机器,我们的用户就可以简洁地表达我们的流程:

import Ops._
val ctxResult = for {
  user <- UserContext(new UserManager{})
  validatedModel <- Context(Ops.prepareModel(user,testModel)).map(Ops.validateModel)
  commitResult <- Context(commitModel(user,validatedModel))
} yield commitResult

该过程的结果仍然是封装的,可以使用value方法从Context中“取出”:

val result = ctxResult.value

请注意,我们需要将业务操作封装到要在此monadic组合中使用的Context中.还要注意,我们不需要手动创建也不需要清理用于操作的用户.这是为我们照顾的.

此外,如果我们需要多种托管资源,则可以使用此方法通过将不同的上下文组合在一起来管理其他资源.

有了这个,我只想为问题提供另一个角度.管道更复杂,但它为用户创建了一个坚实的基础,通过组合创建安全的流程.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读