惯用的元编程

发布于 2024-10-06 15:51:44 字数 1644 浏览 1 评论 0原文

虽然网络上有大量资源赞扬 Groovy 的无数元编程功能,但我还没有找到任何接近于实际使用这些功能的全面“最佳实践”指南的内容。

除了典型的过度使用的“买者自负”警告之外,我读过的最具体的建议建议尽可能使用类别来支持增强元类(这实际上只是强化旧习惯用法的另一种方式) “范围有限”)。

常识对于我的琐碎项目来说已经足够了,但当我处理更雄心勃勃的任务时,我越来越担心从可能较差/不一致的先例中进行构建。

因此,我非常感谢任何关于 Groovy(甚至是与语言无关的——我对 Ruby 的短暂经验也让我同样想要)元编程最佳实践的建议、资源或具体示例。

为了澄清这个主题,我将提供一个(高度)简化的有理数项目,该项目可以通过几种不同的方式使用元编程:

@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)

然而,尝试 3*new Rational(num:1, den:2) 将显然会产生 MissingMethodException。

添加通信属性的最简单且可以说是最不脆弱的方法是使用 Rational 类中的静态初始化程序块:

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

但这实际上是全局的,并且相当严格。

一种更通用、或许更有组织的方法是使用某种可选的引导:

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

现在我们可以选择启用我们希望拥有的功能。然而,这可能会导致各种依赖性问题。

然后是类别......安全明确,但不太方便:

@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)
}

一般来说,我发现当我添加新行为时我会修改元类,但当我更改时使用类别em> 现有行为。例如,重写除法运算符以生成分数最好包含在类别内。因此,解决方案 1 或 2 是“可接受的”,因为我只是将行为附加到 Integer 类,而不改变典型用法。

有人同意/不同意这种观点吗?或者也许知道一些更好的方法? (我意识到我在这里省略了 mixin。)

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 methodology? (I omitted mixins here, I realize.)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

凝望流年 2024-10-13 15:51:44

Groovy in Action 中有一个关于元编程的章节,很好地涵盖了这个主题。请务必获得第二版(印刷版尚不可用,但您可以获得电子格式的早期访问版本),因为第一版已经过时了,特别是在元编程主题方面。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文