代码之家  ›  专栏  ›  技术社区  ›  zella

如何正确等待多条消息,并在之后成为新状态

  •  0
  • zella  · 技术社区  · 8 年前

    def waitInitialisation(@Nullable one: Integer, @Nullable two: String): Receive = {
      case _one: Int =>
        if (two == null)
          context.become(waitInitialisation(_one, two))
        else {
          doSomething()
          context.become(initialised(_one, two))
        }
      case _two: String =>
        if (one == null)
          context.become(waitInitialisation(one, _two))
        else {
          doSomething()
          context.become(initialised(one, _two))
        }
    }    
    
    def initialised(one: Int, two: String): Receive = ???
    
    override def receive: Receive = waitInitialisation(null, null)
    

    我看到的问题是:空检查和重复代码。我如何简化我的实现并使其正确?

    4 回复  |  直到 8 年前
        1
  •  1
  •   Andrei T.    8 年前

    @chunjef已经在使用选项方面给出了一个很好的方向,这是Scala的发展方向。我在下面介绍另外两个选项,其中我使用模式匹配使代码更加美观。

    在查看这两种解决方案之前,请记住通常 .get Option 不建议使用,这可能会给您带来一些编译器警告。无论如何,在我们的示例中,我们总是确保做出正确的选择,因为我们要事先检查选项是否正确 isDefined .

    哦,当与可能出现的价值观合作时 null 使用选项的应用,而不是一些应用。


    第一种方法基本上定义了相同的方法,但结构有所不同:

    def waitInit(one: Option[Int], two: Option[String]): Receive = {
      case value: Int if two.isDefined =>
        context.become(initialised(value, two.get))
      case value: Int =>
        context.become(waitInit(Option(value), two))
      case value: String if one.isDefined =>
        context.become(initialised(one.get, value))
      case value: String =>
        context.become(waitInit(one, Option(value)))
    }
    
    override val receive = waitInit(None, None)
    

    第二种方法将此逻辑分为两部分,以便您可以更轻松地继续:

    def waitOne(two: Option[String]): Receive = {
      case one: Int if two.isDefined =>
        context.become(initialised(one, two.get))
      case one: Int =>
        context.become(waitOne(two) orElse waitTwo(Option(one)))
    }
    
    def waitTwo(one: Option[Int]): Receive = {
      case two: String if one.isDefined =>
        context.become(initialised(one.get, two))
      case two: String =>
        context.become(waitOne(Option(two)) orElse waitTwo(one))
    }
    
    override val receive: Receive =
      waitOne(None) orElse waitTwo(None)
    

    就是这样,我没有在这里放一些代码(比如 initialised )基本上是因为它是一样的。

    享受:)

        2
  •  0
  •   Jeffrey Chung    8 年前

    Option 是Scala中处理null的惯用方法:

    def waitInitialisation(one: Option[Int], two: Option[String]): Receive = {
      case _one: Int =>
        two match {
          case Some(s) =>
            doSomething()
            context.become(initialised(_one, s))
          case None =>
            context.become(waitInitialisation(Option(_one), None))
        }
    
      case _two: String =>
        one match {
          case Some(i) =>
            doSomething()
            context.become(initialised(i, _two))
          case None =>
            context.become(waitInitialisation(None, Option(_two)))
        }
    }
    
    def initialised(one: Int, two: String): Receive = ???
    
    def receive = waitInitialisation(None, None)
    

    至于“代码复制”,我不会挂断电话 become

    • waitInitialisation(None, None)
    • waitInitialisation(Some, None)
    • waitInitialisation(None, Some)
    • initialised

    您可能可以使用 FSM

        3
  •  0
  •   volia17    8 年前

    您还可以通过附加消息来实现这一点。

    case class InitState(
        one : Option[Int], 
        two : Option[String], 
        three : Option[Boolean]      
    )
    {
    
      def fire() : Unit = {
        context.become(waitInit(this))
        self ! this
      }
    }
    
    def waitInit(st : InitState = InitState(None, None, None)) : Receive = {
      case i : Int =>       
        st.copy(one = Some(i)).fire()
      case s : String => 
        st.copy( two = Some(s)).fire()
      case b : Boolean =>
        st.copy(three = Some(b)).fire()
    
      case InitState(Some(i : Int), Some(s : String), Some(b : Boolean)) =>
        context.become(afterInit(i, s, b))
    
      case _ : InitState =>  
    
    }
    
    def afterInit(one : Int, two : String, three : Boolean) : Receive = ???    
    def receive = waitInit()
    

    正如chunjef所写,对于不那么简单的情况,最好的选择是使用FSM。

        4
  •  0
  •   hveiga    8 年前

    akka-contrib 提供了与您正在寻找的类似的聚合器模式: https://github.com/akka/akka/blob/master/akka-contrib/src/main/scala/akka/contrib/pattern/Aggregator.scala

    此代码现在已弃用,但您可以将其复制到项目中。

    在这里,您可以找到它的工作原理(此文档非常古老): http://doc.akka.io/docs/akka/2.3.0/contrib/aggregator.html

    expect expectOnce

    推荐文章