How to call the correct method in Scala/Java based the types of two objects without using a switch statement?

前端 未结 5 1147
星月不相逢
星月不相逢 2020-12-28 22:28

I am currently developing a game in Scala where I have a number of entities (e.g. GunBattery, Squadron, EnemyShip, EnemyFighter) that all inherit from a GameEntity class. Ga

5条回答
  •  执念已碎
    2020-12-28 23:05

    Chain of Responsibility

    A standard mechanism for this (not scala-specific) is a chain of handlers. For example:

    trait Handler[Msg] {
      handle(msg: Msg)
    }
    

    Then your entities just need to manage a list of handlers:

    abstract class AbstractEntity {
    
        def handlers: List[Handler]
    
        def receive(msg: Msg) { handlers foreach handle }
    }
    

    Then your entities can declare the handlers inline, as follows:

    class Tank {
    
       lazy val handlers = List(
         new Handler {
           def handle(msg: Msg) = msg match {
             case ied: IedMsg => //handle
             case _           => //no-op
           }
         },
         new Handler {
           def handle(msg: Msg) = msg match {
             case ef: EngineFailureMsg => //handle
             case _                    => //no-op
           }
         }
       )
    

    Of course the disadvantage here is that you lose readability, and you still have to remember the boilerplate which is a no-op catch-all case for each handler.

    Actors

    Personally I would stick with the duplication. What you have at the moment looks a lot like treating each entity as if it is an Actor. For example:

    class Tank extends Entity with Actor {
    
      def act() { 
        loop {
          react {
             case ied: IedMsg           => //handle
             case ied: EngineFailureMsg => //handle
             case _                     => //no-op
          }
        }
      }
    }
    

    At least here you get into the habit of adding a case statement within the react loop. This can call another method in your actor class which takes the appropriate action. Of course, the benefit of this is that you take advantage of the concurrency model provided by the actor paradigm. You end up with a loop which looks like this:

    react {
       case ied: IedMsg           => _explosion(ied)
       case efm: EngineFailureMsg => _engineFailure(efm)
       case _                     => 
    }
    

    You might want to look at akka, which offers a more performant actor system with more configurable behaviour and more concurrency primitives (STM, agents, transactors etc)

提交回复
热议问题