I usually use traits to implement strategies (some actions that do not require accompanying fields). I recently discovered that the same functionality can be defined in terms of objects. They can directly expand a functional attribute or extend some feature that defines special methods, except apply ()
Code example:
/* strategy realization via traits */ package object Traitness { trait Strategy { def performAction() : Unit = () } abstract class Usage {_ : Strategy => def doWork() = this.performAction() } // defining strategies trait SayA extends Strategy { override def performAction() = { println("A") super.performAction() } } trait SayB extends Strategy { override def performAction() = { println("B") super.performAction() } } trait SayC extends Strategy { override def performAction() = { println("C") super.performAction() } } //using strategies class SimpleStrategy extends Usage with SayA def reverseOrder() = new Usage with SayC with SayA object fullUsage extends Usage with SayA with SayB with SayC //run-time checking val check1 : Boolean = (new SimpleStrategy).isInstanceOf[SayB] val check2 : Boolean = reverseOrder().isInstanceOf[SayB] val check3 : Boolean = fullUsage.isInstanceOf[SayB] //compile-time checking def proclaim(x : SayB) = println("SayB") } /* strategy realization via function objects */ package object Valueness { trait Strategy extends Function0[Unit] class Usage(val strategies : List[Strategy]) { def doWork() = for (s <- strategies) s() } //defining strategies object SayA extends Strategy { override def apply() = { println("A") } } object SayB extends Strategy { override def apply() = { println("B") } } object SayC extends Strategy { override def apply() = { println("C") } } //using strategies class SimpleStrategy extends Usage(SayA :: Nil) def reverseOrder() = new Usage(SayB :: SayA :: Nil) val fullUsage = new Usage(SayA :: SayB :: SayC :: Nil) //run-time checking def check(strategy : Strategy, usage : Usage) = usage.strategies contains strategy val check1 : Boolean = check(SayB, new SimpleStrategy) val check2 : Boolean = check(SayB, reverseOrder()) val check3 : Boolean = check(SayB, fullUsage) //no compile-time checking available }
Which one to choose?
source share