Monday, November 24, 2008

Groovy's Design by capabilty or (duck typing) to the rescue

One of the many cool features available in Groovy is "Duck Typing". The phrase comes from "If it walks like a duck, if it talks like a duck then it is probably a duck".

Because groovy is dynamic, it allow you to call methods on an object without knowing until runtime if that method exists. So this has helped me this weekend on one of my Grails projects.

I have two domain objects.

Stamp and CustomStamp

They are similar but different. Anyway they currently both have a method called:

createStampText(String name,String email)

I then have a method on a Grails Service class that takes a Stamp and some other parameters to construct the desired output.

This service method should not need to care if a Stamp or a CustomStamp is passed in. So how to do this?

Well in Java i would introduce an interface that contains the method:
createStampText(String name,String email) maybe called IStamp
Then both Stamp and CustomStamp could implement it. Then my service method could be like this:

service.doSomething(IStamp stamp,String other,String more){
stamp.createStampText(name,email)
}

But already this is maybe the norm in the pure java world but it is NOT the groovy way and is quite frankly "Yak Shaving" just to get the method called polymorphically.

But using Groovy which my service object are thanks to Grails i can use Duck Typing. Now to use this i simply remove the type. So my service method becomes something like this:

service.doSomething(stamp,String other,String more){
stamp.createStampText(name,email)
}

This way i do not have to create a new interface and i do not have to modify my two domain objects. The service method will just try to call createStampText on any object that is passed in as the first parameter.

Now that is shweet !!

10 comments:

Matthew Taylor said...

That is, indeed, shweet. I haven't written one interface since I've been using Groovy and Grails.

Brock Heinz said...

... taking it a step further and providing a run time implementation of the 'createStampText' is a powerful way to aid in testing as well.

Consider the following:

boolean created = false
def ss = [createStampText: {name, email -> created = true} as Stamp
service.doSomething(ss, "Name", "eml")
assert created

Following that pattern will ensure that your service implementation will know at test time should the signature (or name) of your 'createStampText' method change.

Good stuff :)
Brock

Peter Delahunty said...

Hi Brock

Yep that is nice. I seen something like that in the "Groovy Programming" book i am currently reading. All seems to good to be true after 10 years of java :)

Greg said...

Declaring an interface communicates your intent to the reader in a verifiable way. It has a direct purpose.

The efforts the reader must go to in order to understand your code without the interface are yak-shaving.

The unit tests you must write to ensure that noone passes in a non-duck are yak-shaving.

Writing an interface is not yak-shaving.

Modifying your existing domain objects to meet the interface is hahajavasux. :)

Ricky Clarkson said...

You might like Scala's structural typing then. It has all the performance of Groovy (it's slow because it uses reflection to get dispatch done) but if it's going to fail it'll fail at compile time.

Peter Delahunty said...

Hi Greg

You are right an interface would communicate the intent to the user.

But so could javadoc or in my case a simple relevant name for the variable.

But lets be pragmatic here. I am not writing a public API just some code in my app. Where if any other programmer were to use they would have the source code and could see the intent.

So is it right to do this? Yes in some cases in but in others you may need to be more strict. But is static typing really more strict ?

This compiles

String test = "blah";
service.((IStamp)test);

won't run though :)

Ricky Clarkson said...

"But is static typing really more strict"?

Yes.

Static typing proves that certain runtime errors are impossible. Each type system aims at certain classes of error though. Java's doesn't eliminate all ClassCastExceptions, though the compiler will fail for some obviously incorrect casts, e.g., (String)5.

Greg said...

Good example of where static typing _would_ be more strict. Type-casting is more hahajavasux for you. Which is not to say that dynamic typing itself sux per se, but having a dynamic hole that big in your static type system is pretty laughable.

Peter Delahunty said...

So Greg

I am keen to know what superior language you use to solve real world problems where you don't have these "sux" problems.

Here is my guess. It is a "laboratory language" as in: works perfect with perfect fabricated problems in the lab. But has never seen the light of day in the real world.

Please do tell :)

Ricky Clarkson said...

Peter,

One such language is Java without casts. Another is C# without casts. Another is Scala without casts. I'm sure you get the idea.

Haskell also has casts, but normal Haskell programs don't require casts because Haskell doesn't have subtyping and Haskell doesn't have programmers who think OO is useful.