只做一次,没有状态

时间:2013-10-19 18:22:21

标签: scala functional-programming state

我有以下情况:

在初始化(实际上是第一次接收)套接字时,我想检查握手(TLS)中的某些内容,这必须仅在连接初始化时检查,而不是在每次进一步接收时检查。

目前我有一个奇怪的地方:

   // this is happening outer scope
   var somethingThatGetsComputedinInit = 0

   def receive {
      if (init) {
        somethingThatGetsComputedinInit = doinitstuff(StuffIOnlyGetInitially)
        init = false
      }
    }

虽然它会起作用,但这种气味非常危险。什么是纯粹的功能解决方案?

5 个答案:

答案 0 :(得分:10)

在这种情况下,您希望在scala中使用lazy val修饰符。这在Twitter's Effective Scala中有所建议。请考虑对您问题中的示例进行以下编辑。

class Foo {
    def doinitstuff() : Int = {
        println("I'm only going to be called once")
        42
    }

    lazy val somethingThatGetsComputedinInit = doinitstuff()

    def receive {
        println(somethingThatGetsComputedinInit)
    }
}

并且Foo调用多次接收实例的客户端将输出以下内容:

 val foo = new Foo                               //> foo  : worksheet.Foo = worksheet.Foo@5853c95f
  foo.receive                                     //> I'm only going to be called once
                                                  //| 42

  foo.receive                                     //> 42
  foo.receive                                     //> 42

答案 1 :(得分:4)

在您的具体示例中,由于您正在使用actor,因此您实际上可以使用"context.become and context.unbecome"替换其实现来建模状态机。在此基础上有一个抽象层Akka FSM,它为完成此类事物提供了更好的语法。

部分取消Akka FSM docs

的示例
sealed trait State
case object Initializing extends State
case object Initialized extends State

class Socket extends Actor with FSM[State, Option[Client]] {

  startWith(Initializing, None)

  when(Initializing) {
    case Event(msg: Connect, _) => createClient(msg).fold(stay) {
      client => 
        //Do more stuff
        goto(Initialized) using Some(client)
    }
  }

  when(Initialized) {
    case Event(msg: Receive, data@Some(client)) => 
      //Do more stuff using client
      stay using data
  }

  initialize()

}

答案 2 :(得分:1)

很棒的问题。

你一定要阅读状态模式

an example,摘自Eric Gamma Desing模式书*,与您一样,使用TCP连接。它不是函数式编程,但可以作为指导。

*设计模式的参考指南,hoverer我不推荐这本书,而是强烈鼓励你阅读Head First:设计模式,它具有更强大的教学工具,可以邀请你参与设计原则,更为重要比模式(和范例)。

希望它有所帮助!

答案 3 :(得分:0)

您还可以考虑将var与函数回调一起使用,并将var更新为在第一次之后不执行任何操作:

   var firstTimeInit = StuffIOnlyGetInitially => doinitstuff(StuffIOnlyGetInitially)

   // this is happening outer scope
   var somethingThatGetsComputedinInit = 0

   def receive {
        somethingThatGetsComputedinInit = firstTimeInit(StuffIOnlyGetInitially)
        firstTimeInit = StuffIOnlyGetInitially => ()
    }

答案 4 :(得分:-1)

听起来像Scala“lazy val”的好用例。

lazy val somethingThatGetsComputedinInit = doinitstuff()

您可以保证val在首次使用时仅初始化一次。我不知道你的代码在哪里,如果它是正确的位置,但如果没有,你可以只参考“接收”里面的val来强制初始化。