Sunday, April 11, 2010

Groovy Metaprogramming: propertyMissing

The other day, while working on a Java project, I realized that I could implement an application requirement quite quickly by extending a current domain object and adding an attribute. This is an enterprise wide, industry standard domain object model so I couldn't just add the attribute to the domain object without cutting through some red tape. Plus, it was an attribute that I needed for my application and it would most likely have no use in other projects. So, I had something like this:
public class Policy {

    private String policyNumber;
    private Double value;
    private Double interestRate;

    /* getters and setters */
    ....
}

Then to get things done, I went ahead and created:
public class MyPolicy extends Policy {

    private String myNewProperty;
    private Policy p;

    public MyPolicy(Policy policy) {
        this.p = policy;
    }

    /* getters and setters */
    ....
}

Now, I could fulfill the requirement with ease because I now had a Policy object with the extra attribute I needed, myNewProperty, all in the MyPolicy object. I could now handle the Policy returned from the Web service call and pass it into the MyPolicy constructor, do some work to create an instance of MyPolicy with a Policy object, derive the value of myNewProperty and then send it on to a view for example. Nice, I think that will work for my application.

Later, I thought about how nice it would have been if the Policy object was implemented with Groovy. Then I could take advantage of Groovy's metaprogramming features like propertyMissing. When I have propertyMissing in my language arsenal, I can create the Policy object like so:

class Policy {

    def properties = Collections.synchronizedMap([:])

    String policyNumber
    Double value
    Double interestRate

    def propertyMissing(String name, value)  { properties[name] = value }

    def propertyMissing(String name) { properties[name] }
}

In the implementation above, the propertyMissing(String name, value) method is called when trying to set a value, myNewProperty, that doesn't exist in the Policy object. The propertyMissing(String name) method is called when trying to get a property, myNewProperty, that doesn't exist in the Policy object. By default, the propertyMissing(String name) will return null if the value was never initialized or never dynamically created. Yeah, it is an incredible feature. What is really nice is that I didn't have to create the MyPolicy object at all! In the Groovy world I could write the following:

def p = new Policy()

p.policyNumber = "12345678"
p.value = 12000.00
p.interestRate = 2.3

println p.policyNumber
println p.value
println p.interestRate

/* new property */
p.myNewProperty = "Active"

println p.myNewProperty


Later on I could even write:

/* another new property */
p.myOtherNewProperty = "Wow"

println p.myOtherNewProperty

This would save me some time and now because the Policy object is expandable, other applications that need to extend the Policy object, for application purposes, will be able to utilize these features. I am convinced, Groovy IS productivity for Java.