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?
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.