Saturday, March 29, 2008

Design Patterns in Scala

Design patterns in Scala

The GoF Design Patterns are perhaps the most popular design fads of Software ever. However some people argue that they are more or less solutions to the deficiencies of Language itself (particularly C++/Java) and if the language is powerful enough they are redundant. Consider this excellent presentation from Peter Norvig for example.
I agree that patterns translate to quite natural and concise code in some languages, but the intend of the pattern may still be important. So lets consider some of the popular patterns in Scala - a language which like Ruby/Lisp etc reduces Pattern implementation to triviality (though its not a dynamic language!).

1. Singleton
Singleton is one of the frequently (over)used patterns. In spite of being the simplest of patterns, it is surprisingly difficult to implement a 'pure' singleton in Java. The particularly famous Double-checked locking and so on. However, in Scala it is trivial. Lets say you need to create a Registry object which is singleton. You can do that in the following way:

object Registry {
def getEntry(): Entry {
}
//Other fields/methods
}
//Create and use the singleton
val entry = Registry.getEntry


Thats it! The only difference from defining a normal class is that instead of the 'class' keyword we used 'object'. If you have to map it back to java, this is equivalent of defining a class and all its fields/methods are static.

2. Strategy
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)


3. Factory
Factory pattern addresses the fact that object constructors cannot return arbitrary objects. If you call constructor of object A, you always get an object A. If you need different objects based on parameter types, you typically create a Singleton Factory. In Scala also you need to do the same thing, but in a bit more elegant manner.

object Car {
def apply(String type) {
type match {
case "Race" => new RaceCar();
case "Normal" => new NormalCar();
case _ => throw new Exception;
}
}
}
//And you can create cars like:
val myCar = Car("Race")
//instead of more verbose
//Car myCar = CarFactory.getInstance().createCar("Ferrari");

Basically, you can exploit the fact that what apparently looks like a constructor call to a singleton object is syntactic sugar for the "apply" method.
4. Visitor
Visitor pattern is considered harmful by many people, but still it has its value at specific cases. Lets see a typical java implementation of Visitor pattern:

abstract class Expression {
...
public void accept(Visitor v);
}

class Identifier extends Expression {
}
class Sum extends Expression {
}
..

class Visitor {
public void visit(Identifier i) { }
public void visit(Sum s) { }
}

Typical java boilerplate code as you would expect. In Scala you can achieve the same result by using pattern matching:

trait Expression {
...
}

case class Identifier(value: Int) extends Expression {
}
case class Sum extends Expression {
}

object EvalVisitor {
def visit(expr: Expression): Int = expr match {
case (Identifier(v)) => v
case (Sum(e1, e2)) => visit(e1) + visit(e2)
}
}

The pattern matching code is also more flexible.
5. Decorator
Finally consider the Decorator pattern. Since Scala supports mixins and implicit conversions, Decorator is also simple and natural. Conceptually, there are two ways you can decorate an object - by adding new functionality and extending existing functionality.
To decorate a class with new functionality, you can use implicit conversions. Consider the RichInt class in scala standard library in action:

val range1to10 = 1 to 10

which in scala translates to 1.to(10). However, there is no method "to" in Int class. So how does it work? Scala has an innovative concept called Implicits for pimping your library. In essence it is Ruby's open classes but lexically scoped. Its interesting stuff and you can read about it elsewhere. For us, it suffices to say that there is an implicit conversion in Predef [implicit def intWrapper(x : Int) : RichInt] that does the trick.
Mixins are also good for decorating your classes and extend its functionality in different dimensions. For example consider the typical Reader interface:

trait Reader {
type T
def read: T
}

trait SynchronizedReader extends Reader {
abstract override def read: T = synchronized(super.next)
}

trait BufferedReader extends Reader {
abstract override def read: T = {
//buffering code
super.read
}
}

//A concrete implementation
class FileReader extends Reader {
type T = char
def read: char = ..
}

//Now we can mix in stuff that we need
//Create a FileReader
val f = new FileReader
//create a fileReader which is synchronized
val syncReader = new FileReader with SynchronizedReader
//create a fileReader which is synchronized and buffered
val bsReader = new FileReader with BufferedReader with SynchronizedReader


Conclusion
Design Patterns are good recipes for designing software. However, most of them generally solve a language issue than a design issue. If you have a good Language patterns (at least most of them) will become trivial. For example, in a structured language, the concepts of virtual methods or classes may be a 'Design Pattern'. Once you move to a powerful language, the design patterns that you deal with will also change. They will be at a higher level of abstraction. More on it later!

12 comments:

Robert O'Connor said...

good post

Shawn Hartsock said...

Nice, that clears up a few ideas for me.

Annas "Andy" Maleh said...

Great post. I agree that as older patterns become basic constructs in newer languages, new patterns emerge at higher levels of abstraction.

Anonymous said...

i dont agree that just because language is absorbing "design pattern", it was a language pattern and not design pattern.

bodhi said...

Anonymous: As I've mentioned in the post, the intend of the pattern still holds. Its only that the 'pattern' is obvious to mention. For eg. in C, OOP would be a design pattern. But in C++ it is a norm.

ghkj said...
This comment has been removed by a blog administrator.
Anonymous said...

I found this site using [url=http://google.com]google.com[/url] And i want to thank you for your work. You have done really very good site. Great work, great site! Thank you!

Sorry for offtopic

Olle K said...

Good work, but the examples could use some debugging:

1. For the strategy pattern I think the last line should be:

"calculateTax(npo, ((t: TaxPayer) => 0))"

2. For the factory pattern there are some errors. To make the example runnable you could make it look like this:

sealed abstract class Car {
override def toString = "YY Car"
}
case class RaceCar() extends Car{
override def toString = "YY RaceCar"
}
case class NormalCar() extends Car{
override def toString = "YY NormalCar"
}

object Car {
def apply(x: String): Car = {
x match {
case "Race" => new RaceCar();
case "Normal" => new NormalCar();
}
}
}

Then when you run the examples you can verify that the correct class has been created:

scala> val olles = Car("Race")
olles: Car = YY RaceCar

scala> olles.toString
res0: java.lang.String = YY RaceCar

Peter said...

what a interesting information, I gonna try this if doesn't work I tell so fast as I can, with this and viagra online maybe I can fix one or two problem that I have.

casino said...

Fantastic factor of perspective, I certainly not thought like this but you’ve opened my brain...

drug detox

Liberty Travel said...

You can still see the unemployment levels are high even after a few years of the recession first hitting. I like the fact that they try to call it a double dip recession vs calling it a depression but if it goes down for travel a couple years, then goes up briefly should it "count" as wiping the slate clean...You can see the effects in the honeymoons industry as people did not have the disposable income to book cruises or other activities that cost a pretty heft entertainment fund. Same is true with global brands joining other brands such as flight center to recapture lost market share...whatever happens people need jobs or they will begin to riot...once the unemployment checks stop.

Karlo tagalog said...

Quite possibly the most succinct and current info I came across about this subject. Sure pleased that I discovered that site by accident. I’ll probably be subscribing for your feed so that I will get the most current updates. Like the information here.
bank levy