开发者

Idiomatic MetaProgramming

While the web abounds with resources extolling the myriad metaprogramming capabilities of Groovy, I've yet to find anything close to a comprehensive "best-practices" guide for the actual use of such features.

Aside from the typical caveat emptor warning of excessive usage, the most specific piece of advice I've read suggests using categories in favor of augmenting metaclasses when possible (which is really just another way of reinforcing the old idiom of 'limited scope').

Common sense has been sufficient for my trivial projects, but I'm increasingly concerned about building from potentially poor / inconsistent precedents as I tackle more ambitious tasks.

Thus, I would greatly appreciate any advice, resources, or concrete examples of Groovy (or even language-agnostic - My admittedly brief experience with Ruby left me similarly wanting) metaprogramming best-practices.

To clarify the topic, I'll provide a (highly) simplified Rational number project that could employ metaprogramming in several different ways:

@Immutable class Rational{
    int num, den

    Rational multiply(Integer v){
        new Rational(num:num*v, den:den)
    }
}
assert new Rational(num:1, den:2) * 3 == new Rational(num:3, den:2)

Yet, attempting 3*new Rational(num:1, den:2) would obviously produce a MissingMethodException.

The simplest, and arguably least fragile means of adding the communicative property would be with a static initializer block in the Rational class:

static {
    Integer.metaClass.multiply = {Rational fraction -> fraction*delegate}
}
...
assert 3*new Rational(num:1, den:2) == new Rational(num:3, den:2)

But this is consequently global in effect, and rather rigid.

A more versatile, and perhaps more organized approach would be with some kind of optional bootstrapping:

class BootStrap{
    static void initialize(){
        Integer.metaClass.multiply = {Rational fraction -> fraction*delegate}
    }
}

Now we have the option of enabling the feature(s) we wish to have. This, however, could result in all manner of dependency issues.

And then there's categories.. Safely explicit, but not exactly convenient:

@Category(Integer) class RationalCategory{
    Rational multiply(Rational frac){
        frac*this
    }
}
use(RationalCategory){
    assert 3*new Rational(num:1, den:2) == new Rational(num:3, den:2)
}

In general, I find I modify metaclasses when I'm adding new behavior, but use categories when I may be changing existing behavior. Overriding the division operator to produce a Fraction, for instance, would be best contained inside a category. Thus, solutions 1 or 2 would be 'acceptable' as I'm merely appending behavior to the Integer class, not altering typical usage.

Does anyone agree/disagree with that sentiment? Or perhaps know of some superior me开发者_JAVA百科thodology? (I omitted mixins here, I realize.)


There's a chapter on metaprogramming in Groovy in Action, which covers the topic pretty well. Be sure to get the second edition (the printed version is not available yet, but you can get an early access release in electronic format), because the first edition is very out-of-date, particularly on the subject of metaprogramming.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜