Sunday, November 13, 2011

Groovy: A Look at Mixins

Groovy's XML support is often cited as one of the bigger attractions of the language. I often find myself writing classes that parse XML messages of the form:

    class MyXmlParser {
def parse(String response) {
def parsedXml = parsed(response)
doSomethingWith(parsedXml)
}

def parsed(String response) {
new XmlSlurper().parseText(response)
}

// ...
}

One of the things that bothers me about the above pattern is the duplication in the parsed method that might spread throughout a group of classes. Using an inheritance scheme here might jump out as a possible solution at first, but that doesn't sit so well with me, as although the classes that use the duplicated method all parse XML, they all may not serve a similar substitutable purpose (one may send parsed results to a database, while another transforms the XML into an object, for example).

A newer feature of Groovy that I've been meaning to look into is the Mixin Transformation. Here, I could mixin the parsed method to all my classes that need to parse some XML. I started with the example linked to the left, but ran into a couple problems doing joint-compilation with Maven. Before we take a look at that, I'll demonstrate the mixin approach I used for this case.

The first step to adding mixins is to ensure the class you want to have make use of the mixin implement a common interface:

    package prystasj.groovy.xml

interface XmlParser {
def parse(String xml)
}

class MyXmlParser implements XmlParser {
def parse(String response) {
def parsedXml = parsed(response)
doSomethingWith(parsedXml)
}
// ...
}

The next step was to define the mixin class using the @Category annotation:

    package prystasj.groovy.xml

@Category(XmlParser)
class XmlParsing {
def parsed(String response) {
new XmlSlurper().parseText(response)
}
}

Finally, we can apply the @Mixin annotation and remove the parsed method for our parser:

    package prystasj.groovy.xml

@Mixin(XmlParsing)
class MyXmlParser implements XmlParser {
def parse(String response) {
def parsedXml = parsed(response)
doSomethingWith(parsedXml)
}
// ... method parsed(String response) removed ...
}

The MyXmlParser class now has the parsed method mixed in and no longer needs to define it. Is this a worthwile use of a mixin however, i.e. is the overhead worth having the duplication removed? In a more concrete situation, we might have many more duplicated methods between a group of classes that we would could add to the mixin, improving its worth.

Now back to the compilation problems I referred to earlier. When I went to compile, the GMaven plugin created the stubs required for joint compilation, and the Maven Compiler Plugin stumbled:


[ERROR] /home/prystasj/.../target/generated-sources/groovy-stubs/main/prystasj/groovy/xml/MyXmlParser.java:[..] cannot find symbol
[ERROR] symbol: variable XmlParsing

Everything in the stub looked legitimate, so I was rather stumped. I remembered running into a similar problem before, so I went ahead and tried fully qualifying the references inside the annotations:

    package prystasj.groovy.xml

@Category(prystasj.groovy.xml.XmlParser)
class XmlParsing {
def parsed(String response) {
new XmlSlurper().parseText(response)
}
}

@Mixin(prystasj.groovy.xml.XmlParsing)
class MyXmlParser implements XmlParser {
def parse(String response) {
def parsedXml = parsed(response)
doSomethingWith(parsedXml)
}
// ... method parsed(String response) removed ...
}

This resolved the issue and the project compile happily. If I'm not missing something, maybe this might be something worthwile to bring up with the GMaven developers? Thoughts? Thanks for reading.

No comments:

Post a Comment