Groovy is a versatile & very powerful language to code with, as a Java super set it enables a Java programmer to write its code with all the known Java idioms & progress into Groovy land in small steps, in this post ill present two techniques that I find to be very powerful.
The first is closure initialization, in Java land its common that when we have an object A which uses another object B with non trivial construction code (which depends on A's input) than we write all the initialization code of B inside A constructor:
class ComplexInit { // our B
// ... some state that need to be initialized by users of this class
}
class UsesComplex {// our A
private ComplexInit v
def UsesComplex(){
v = new ComplexInit()
//.. some complex initialization logic which depends on UsesComplex
}
}
Another common practice is to create a factory that will encapsulate the initialization of ComplexInit class taking A as input:
class ComplexInitFactory {
def createComplexWith(UsesComplex u){
// initialization code which depends on
}
}
Groovy offers another way which I named "closure initialization":
class UsesComplex{
private ComplexInit val = {
def result = new ComplexInit()
// more logic here
}()
def UsesComplex(){
// ComplexInit is initialized above
}
}
Note that we are embedding a closure within the UsesComplex class & call it immediately after it has been defined, this resembles static initializer code blocks in Java except that it not static at all, the anonymous closure has access to its surrounding scope & can access other data members during its run:
class UsesComplex{
def today = new Date()
private ComplexInit val = {
def result = new ComplexInit()
result.a = today
result
}()
}
By separating our constructors into initialization blocks we modularize the construction of our object, making our code more readable & DRY (no separation between deceleration & initialization).
Another interesting technique is dynamic method dispatch, in Groovy we can dispatch methods like so:
class Target{
def methodA(){ println 'hey'}
}
new Target().methodA()
new Target()."methodA"()// this works too!
This means that we can dispatch methods according to run time logic:
class Target{
def calcA(){'a'}
def calcB(){'b'}
def calcC(){'c'}
def notCalc(){'d'}
}
def t = new Target()
def calcs = t.metaClass.methods.findAll{it.name.startsWith('calc')}.collect{it.name}
calcs.each{println t."$it"()}// will print a b c
Its true that Java reflection has similar capabilities however the Java API is very verbose & not as elegant, use this technique with care since it may result with hard to read code!
Thats about it for this post, let me know if you find these techniques useful in your code.
No comments:
Post a Comment