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?
Answer
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
.
Attribution
Source : Link , Question Author : MattK , Answer Author : Community