This blog is no longer being maintained, please go to The missing link of Agile instead

Wednesday, July 01, 2009

Design patterns in Scala: Adapter

One might think that when considering Adapter pattern the ol' good Java implementation is 'as good as it gets'. But as you're probably already guessing I believe it is not. Surprisingly (ye.. right) Scala shines also on this front.

Say the code we're dealing with is:

class Communism { def makePropaganda = ... }

class Capitalism { def usePublicRelations = ... }

If you wanted to adapt 'Communism' to behave as 'Capitalism' (say we need it during visits of important foreign ministers ;)) in Java typically you'd do it like:

class CapitalismAdapter(val communism : Communism) extends Capitalism {
def usePublicRelations = communism.makePropaganda();
}

// Usage:
new CapitalismAdapter(new Communism).usePublicRelations

If you're in control of 'Capitalism' class it'd be even better to do 'Capitalism' a trait or create 'ICapitalism' trait which'd be implemented by 'Capitalism' class.

Anywho.. Let's do it Scala way.

implicit def systemTransformation(communism : Communism) : Capitalism = new Capitalism {
def usePublicRelations = communism.makePropaganda();
}

// Usage:
new Communism.usePublicRelations

That may seem like a one simple and insignificant improvement - but in fact it's pretty powerful stuff. After all an explicit conversion you did in Java is nothing but noise (by now you most probably noticed that Scala is the noise-annihilator).
Also it saves you from all that extra keystrokes and postpone RSI ;).

And with more difficulty it gets only better. Consider following problem:

For reasons unknown (Architectus Reloadus asked us to do so) we want to extend some lists in our system with a 'fancyMethod'. These conversion should apply only to the lists that contain elements able to be converted to a 'String'.
To do it in Scala you implement an implicit conversion of a 'listToStringList' and put a requirement on a user of providing an implicit conversion of each element to 'String'.

trait StringList { val list : List[String]; def fancyMethod = () }

// implement implicit conversion of 'List[A]' to 'StringList'
implicit def listToStringList[A](aList : List[A])
// require to provide (another) implicit conversion method (A => String) in a scope of this conversion usage
(implicit aToString : A => String) = {
new StringList { val list = aList.map(aToString) }
}

// Usage:
def foo(s : StringList) = ()

// should cause error:
foo(List(1, 2, 3))

implicit def intToString(i : Int) = i.toString

// and now it works:
foo(List(1, 2, 3))

I won't provide the Java solution for this problem just because I care about my sinews..

An important thing to remember is that implicit conversions in Scala are not transitive. It is still possible to do such conversion (with calling any method explicitly) but it must be 'requested' with ':' operator.

class A; class B; class C; class D

implicit def aToB(a : A) = new B
implicit def bToC(b : B) = new C
implicit def cToD(c : C) = new D

// Usage:
val a = new A
println((a:B):C) // A => C
println(((a:B):C):D) // A => D

No comments:

Post a Comment