Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Better alternative to Strategy pattern in Scala?

When I'm programming in Java (or a similar language), I often employ a simple version of the Strategy pattern, using interfaces and implementation classes, to provide runtime-selectable implementations of a particular concept in my code.

As a very contrived example, I might want to have the general concept of an Animal that can make a noise in my Java code, and want to be able to select the type of animal at runtime. So I would write code along these lines:

interface Animal {
    void makeNoise();
}

class Cat extends Animal {
    void makeNoise() { System.out.println("Meow"); }
}

class Dog extends Animal {
    void makeNoise() { System.out.println("Woof"); }
}

class AnimalContainer {
    Animal myAnimal;

    AnimalContainer(String whichOne) {
        if (whichOne.equals("Cat"))
            myAnimal = new Cat();
        else
            myAnimal = new Dog();
    }

    void doAnimalStuff() {
        ...
        // Time for the animal to make a noise
        myAnimal.makeNoise();
        ...
    }

Simple enough. Recently, though, I've been working on a project in Scala and I want to do the same thing. It seems easy enough to do this using traits, with something like this:

trait Animal {
    def makeNoise:Unit
}

class Cat extends Animal {
    override def makeNoise:Unit = println("Meow")
}

class AnimalContainer {
    val myAnimal:Animal = new Cat
    ...
}

However, this seems very Java-like and not very functional--not to mention that traits and interfaces aren't really the same thing. So I'm wondering if there's a more idiomatic way to implement the Strategy pattern--or something like it--in my Scala code so that I can select a concrete implementation of an abstract concept at runtime. Or is using traits the best way to achieve this?

like image 768
MattK Avatar asked Feb 09 '11 21:02

MattK


People also ask

What is the difference between Decorator and strategy pattern?

The strategy pattern allows you to change the implementation of something used at runtime. The decorator pattern allows you augment (or add to) existing functionality with additional functionality at run time.

What is design pattern in Scala?

Design patterns are formalized best practices that the programmer can use to solve common problems when designing an application or system. The classical design patterns are the 23 design patterns by GoF. This project implements dozens of design patterns in Scala.

What type of pattern is strategy?

Strategy is a behavioral design pattern that lets you define a family of algorithms, put each of them into a separate class, and make their objects interchangeable.


3 Answers

Coming from Java, I still like the OO style syntax. I have also just watch the first portion of Deriving Scalaz (Disclaimer) and used this as a little exercise to demonstrate to myself the concepts of Pimp My Library and Implicits. I figured I might as well share my findings. In general there is a little more programming overhead in setting things up this way, but I personally think the usage is cleaner.

This first snippet demonstrates adding the Pimp My Library pattern.

trait TaxPayer

/**
 * This is part of the Pimp My Library pattern which converts any subclass of
 * TaxPayer to type TaxPayerPimp
 */
object TaxPayer {
  implicit def toTaxPayerPimp[T <: TaxPayer](t: T) : TaxPayerPimp[T] =
    new TaxPayerPimp[T] {
      val taxPayer = t
    }
}

/**
 * This is an extra trait defining tax calculation which will be overloaded by
 * individual TaxCalculator strategies.
 */
trait TaxCalculator[T <: TaxPayer] {
  def calculate(t: T) : Long
}

/**
 * This is the other part of the Pimp My Library pattern and is analogus to
 * Scalaz's Identity trait.
 */
trait TaxPayerPimp[T <: TaxPayer] {
  val taxPayer: T
  def calculateTax(tc: TaxCalculator[T]) : Long = tc.calculate(taxPayer)
}


case class Employee(sal: Long) extends TaxPayer

/**
 *  This is the employee companion object which defines the TaxCalculator
 *  strategies.
 */
object Employee {
  object DefaultTaxCalculator extends TaxCalculator[Employee] {
    def calculate(e: Employee) = Math.ceil(e.sal * .3) toLong
  }

  object BelgianTaxCalculator extends TaxCalculator[Employee] {
    def calculate(e: Employee) = Math.ceil(e.sal * .5) toLong
  }
}

case class NonProfitOrg(funds: BigInt) extends TaxPayer

/**
 * This is the NonProfitOrg companion which defines it's own TaxCalculator
 * strategies.
 */
object NonProfitOrg {
  object DefaultTaxCalculator extends TaxCalculator[NonProfitOrg] {
    def calculate(n: NonProfitOrg) = 0
  }
}



object TaxPayerMain extends Application {

  //The result is a more OO style version of VonC's example
  val employee = new Employee(1000)
  employee.calculateTax(Employee.DefaultTaxCalculator)
  employee.calculateTax(Employee.BelgianTaxCalculator)

  val npo = new NonProfitOrg(100000000)
  npo.calculateTax(NonProfitOrg.DefaultTaxCalculator)

  //Note the type saftey, this will not compile
  npo.calculateTax(Employee.DefaultTaxCalculator)

}

We can take this a little bit further using implicits.

trait TaxPayer
object TaxPayer {
  implicit def toTaxPayerPimp[T <: TaxPayer](t: T) : TaxPayerPimp[T] =
      new TaxPayerPimp[T] {
        val taxPayer = t
      }
}

trait TaxCalculator[T <: TaxPayer] {
  def calculate(t: T) : Long
}

/**
 * Here we've added an implicit to the calculateTax function which tells the
 * compiler to look for an implicit TaxCalculator in scope.
 */
trait TaxPayerPimp[T <: TaxPayer] {
  val taxPayer: T
  def calculateTax(implicit tc: TaxCalculator[T]) : Long = tc.calculate(taxPayer)
}

case class Employee(sal: Long) extends TaxPayer

/**
 * Here we've added implicit to the DefaultTaxCalculator.  If in scope
 * and the right type, it will be implicitely used as the parameter in the
 * TaxPayerPimp.calculateTax function.
 *
 *
 */
object Employee {
  implicit object DefaultTaxCalculator extends TaxCalculator[Employee] {
    def calculate(e: Employee) = Math.ceil(e.sal * .3) toLong
  }

  object BelgianTaxCalculator extends TaxCalculator[Employee] {
    def calculate(e: Employee) = Math.ceil(e.sal * .5) toLong
  }
}

/**
 * Added implicit to the DefaultTaxCalculator...
 */
case class NonProfitOrg(funds: BigInt) extends TaxPayer
object NonProfitOrg {
  implicit object DefaultTaxCalculator extends TaxCalculator[NonProfitOrg] {
    def calculate(n: NonProfitOrg) = 0
  }
}

object TaxPayer2 extends Application {

    println("TaxPayer2")

    val taxPayer = new Employee(1000)

    //Now the call to calculateTax will
    //implicitely use Employee.DefaultTaxCalculator
    taxPayer.calculateTax
    //But if we want, we can still explicitely pass in the BelgianTaxCalculator
    taxPayer.calculateTax(Employee.BelgianTaxCalculator)

    val npo = new NonProfitOrg(100000000)

    //implicitely uses NonProfitOrg.defaultCalculator
    npo.calculateTax


}
like image 196
Travis Stevens Avatar answered Oct 04 '22 08:10

Travis Stevens


It could go like that example in "Design pattern in scala":

Like any language where functions are first-class objects or where closures are available, Strategy pattern is obvious.
For eg. consider the 'taxing' example:

trait TaxPayer
case class Employee(sal: Long) extends TaxPayer
case class NonProfitOrg(funds: BigInt) extends TaxPayer

//Consider a generic tax calculation function. (It can be in TaxPayer also).
def calculateTax[T <: TaxPayer](victim: T, taxingStrategy: (T => long)) = {
  taxingStrategy(victim)
}

val employee = new Employee(1000)
//A strategy to calculate tax for employees
def empStrategy(e: Employee) = Math.ceil(e.sal * .3) toLong
calculateTax(employee, empStrategy)

val npo = new NonProfitOrg(100000000)
//The tax calculation strategy for npo is trivial, so we can inline it
calculateTax(nonProfit, ((t: TaxPayer) => 0)

so that I can select a concrete implementation of an abstract concept at runtime.

Here you are using an upper bound in order to restricts the specializations of T in subclasses to those subtypes of TaxPayer.

like image 36
VonC Avatar answered Oct 04 '22 08:10

VonC


You can do a variation on the cake pattern.

trait Animal {
    def makenoise: Unit
}

trait Cat extends Animal {
    override def makeNoise { println("Meow") }
}

trait Dog extends Animal {
    override def makeNoise { println("Woof") }
}

class AnimalContaineer {
    self: Animal =>

    def doAnimalStuff {
         // ...
         makeNoise
         // ...
     }
}

object StrategyExample extends Application {
    val ex1 = new AnimalContainer with Dog
    val ex2 = new AnimalContainer with Cat

    ex1.doAnimalStuff
    ex2.doAnimalStuff
}

In terms of the strategy pattern, the self type on the strategy indicates it must be mixed with a specific implementation of some sort of algorithm.

like image 33
Daniel C. Sobral Avatar answered Oct 04 '22 10:10

Daniel C. Sobral