开发者

How can I create a component-based message handling system with traits?

Here's a short example of what I want to do:

abstract class Message()
case class FooMessage() extends Message
case class BarMessage() extends Message
//... other messages ...

trait Component
{
  def handleMessage(msg: Message):Unit
}

trait ComponentType1 extends Component
{
  abstract override def handleMessage(msg: FooMessage) = {
    //handle foo, pass it up the chain
    super.handleMessage(msg)
  }

  abstract override开发者_运维问答 def handleMessage(msg: BarMessage) = {
    //handle bar, pass it up the chain
    super.handleMessage(msg)
  }
}

//handles some other messages, also might handle the same messages as ComponentType1
trait ComponentType2 extends Component { .. }

Then, these ComponentTypes are mixed in to a class to form an object that is completely composed of modular components.

I have a bunch of different Messages and a bunch of different Components.

  • Not all components handle all message types.
  • Multiple components can handle the same message type.
    • The message cascades up through the components, even if it's handled by another component.
  • A Component can handle more than one message type.

The problem is since handleMessage is defined in Component as accepting a Message, when I try to specialize the msg parameter it doesn't count as an override.

I know one possible solution to this is to declare a big handleMessage method with a big match statement, but I'd like to define a method per message if possible.


Here's a partial function based solution. Tip to CheatEx for the idea.

trait Message   
class FooMessage extends Message 
class BarMessage extends Message 

abstract class Component {
  type CPF = PartialFunction[Message, Unit]

  var pf: CPF  = { case _ => }

  def handleMessage(msg: Message) = pf(msg)
}

trait ComponentType1 extends Component {
  val ct1pf: CPF = {
    case msg: FooMessage => println("foo1")
    case msg: BarMessage => println("bar1")
  }
  pf = ct1pf orElse pf
}

trait ComponentType2 extends Component {
  val parentPf = pf
  val ct2pf: CPF = {
    case msg: 
      BarMessage => println("bar2")
      parentPf(msg)    // cascade 
  }
  pf = ct2pf orElse pf
}

object component1and2 extends ComponentType1 with ComponentType2

component1and2.handleMessage(new FooMessage)
component1and2.handleMessage(new BarMessage)

prints

foo1
bar2
bar1


trait Message   
class FooMessage extends Message 
class BarMessage extends Message 

trait Component {
  def handleMessage(msg: Message) {}
  def handleMessage(msg: FooMessage) {}
  def handleMessage(msg: BarMessage) {}
}

trait ComponentType1 extends Component {
  override def handleMessage(msg: FooMessage) = println("foo1")
  override def handleMessage(msg: BarMessage) = println("bar1")
}

trait ComponentType2 extends Component {
  override def handleMessage(msg: BarMessage) = println("bar2")
}

object component1and2 extends ComponentType1 with ComponentType2

component1and2.handleMessage(new FooMessage)
component1and2.handleMessage(new BarMessage)

prints

foo1
bar2

If you had a list somewhere of all the components in the system you could do

componentList.foreach(c => c.handleMessage(msg))

It would just no-op in Component for the components that didn't handle msg.


For completeness:

trait Message   
class FooMessage extends Message 
class BarMessage extends Message 

trait Component {
  def handleMessage(msg: Message) {}
}

trait ComponentType1 extends Component {
  def handleMessage(msg: Message) {
    msg match {
      case m: FooMessage => println("foo1")
      case m: BarMessage => println("bar1")
      case _ => super.handleMessage(msg)
    }
  }
}

trait ComponentType2 extends Component {
  override def handleMessage(msg: Message) {
    msg match {
      case m: BarMessage =>
        println("bar2")
        super.handleMessage(m)    // cascade 
      case _ => super.handleMessage(msg)
    }
  }
}

object component1and2 extends ComponentType1 with ComponentType2

component1and2.handleMessage(new FooMessage)
component1and2.handleMessage(new BarMessage)

prints

foo
bar2
bar1
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜