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?
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.
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.
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.
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
}
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
.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With