Groovy 中@Delegate、@Mixin 和 Traits 之间的区别? [英] Difference between @Delegate, @Mixin and Traits in Groovy?

查看:29
本文介绍了Groovy 中@Delegate、@Mixin 和 Traits 之间的区别?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有人能解释一下我什么时候想使用 Groovy Traits vs. Mixins (@Mixin) vs. Delegates (@Delegate)?也许一些权衡和设计问题会有所帮助.

Would someone explain when I would want to use Groovy Traits vs. Mixins (@Mixin) vs. Delegates (@Delegate)? Maybe some trade-offs and design concerns would help.

它们似乎都允许重用多个类"行为.谢谢.:-)

They all seem to allow for reusing multiple "classes" of behavior. Thanks. :-)

这个 SO 线程也很有帮助:@Delegate 之间的区别和 Groovy 中的 @Mixin AST 转换

This SO thread was helpful too: Difference between @Delegate and @Mixin AST transformations in Groovy

推荐答案

我同意,它们似乎都允许重用多个类"行为.不过还是有区别的了解这些可能会帮助您做出决定.

I agree, they all seem to allow reusing multiple "classes" of behaviour. There are differences, though, and understanding these will probably aid your decision.

在提供每个功能的简要总结/亮点和合适的示例之前用法,我们就各自总结一下吧.

Before providing a brief summary/highlight of each feature and examples of suitable usage, let's just summarize on the conclusion of each.

结论/典型用法:

  • @Delegate:用于添加委托类的所有功能,但仍避免与实际执行.让您实现组合优于继承.
  • @Mixin:在 groovy 2.3 中已弃用.将一个或多个类中的方法添加到您的类中的简单方法.漏洞百出.
  • Runtime mixin:将一个或多个方法添加到 any 现有类中,例如JDK 或第 3 方库中的类.
  • 特征:groovy 2.3 中的新特性.向您的类添加一个或多个特征的明确定义的方法.替换@Mixin.唯一的在 Java 类中可以看到添加的方法之一.
  • @Delegate: Used to add all the functionality of the delegate class, but still avoid tightly coupling to the actual implementation. Let's you achieve composition over inheritance.
  • @Mixin: Deprecated with groovy 2.3. Simple way to add methods from one or more classes into your class. Bug-ridden.
  • Runtime mixin: Add one or more methods into any existing class, e.g. a class in the JDK or a 3rd party library.
  • Traits: New in groovy 2.3. Well-defined way to add one or more traits to your class. Replaces @Mixin. The only one of these where added methods are visible in Java classes.

现在,让我们更详细地研究其中的每一个.

And now, let's look into each of these with a little bit more detail.

@Delegate

在许多情况下,继承被过度使用.也就是说,它经常被不当使用.Java中的经典示例是扩展输入流、读取器或集合类.对于其中大多数,使用继承也是与实施紧密结合.也就是说,实际的实现是这样编写的,以便公共方法实际上使用了另一个.如果您同时覆盖两者,并调用 super,那么您可能会不受欢迎副作用.如果在以后的版本中实现发生变化,那么您将不得不更新您的处理也是.

Inheritance is over-used in many cases. That is, it is often improperly used. Classic examples in Java are extending input streams, readers or the collection classes.. For most of these, using inheritance is too tightly coupled with the implementation. That is, the actual implementation is written so that one of the public methods actually use another. If you override both, and you call super, then you might get unwanted side-effects. If the implementation changes in a later version, then you will have to update your handling of it as well.

相反,您应该努力使用组合而不是继承.

Instead, you should strive to use composition over inheritance.

例如,对添加到列表中的元素进行计数的计数列表:

Example, a counting list that counts the elements added to a list:

class CountingList<E> {
    int counter = 0
    @Delegate LinkedList<E> list = new LinkedList<>()
    boolean addAll(Collection<? extends E> c) {
        counter += c.size()
        list.addAll(c)
    }
    boolean addAll(int index, Collection<? extends E> c) {
        counter += c.size()
        list.addAll(index, c)
    }
    // more add methods with counter updates
}

在此示例中,@Delegate 删除了您为所有公共方法编写的所有繁琐的样板代码想要保持原样",即添加的方法只是将调用转发到底层列表.此外,CountingList 与实现分离,因此您不必关心其中之一是否方法是通过调用另一个来实现的.在上面的例子中,情况确实如此,因为LinkedList.add(Collection) 调用 LinkedList.add(int, Collection),所以不会那么直接使用继承来实现.

In this example, the @Delegate removes all the tedious boiler-plate code for all public methods that you want to leave "as-is", i.e. methods are added that simply forwards the call to the underlying list. In addition, the CountingList is separated from the implementation so that you don't have to care whether one of these methods is implemented by calling the other. In the example above, that is actually the case, since LinkedList.add(Collection) calls LinkedList.add(int, Collection), so it would not be as straight-forward to implement using inheritance.

总结:

  • 为委托对象中的所有公共方法提供默认实现.
    • 明确添加的具有相同签名的方法优先.
    • 但如果你这样做,你应该考虑这是否真的可取.
    • 钻石问题,即如果您在代表中有多个方法同一个签名?
    • but if you do, you should consider whether that is really desirable.
    • what about the diamond problem, i.e. if you have multiple methods in the delegates with the same signature?
    • CountingList 不是 LinkedList 的实例.
    • I.e. CountingList is not an instance of LinkedList.

    @Mixin

    由于即将推出的特征支持,@Mixin 转换将在 groovy 2.3 中弃用.这提供了一个暗示所有可以用 @Mixin 做的事情,都应该可以用 trait 来做.

    The @Mixin transform will be deprecated with groovy 2.3, due to the upcoming traits support. This provides a hint that everything that is possible to do with @Mixin, should be possible to do with traits instead.

    根据我的经验,@Mixin 有点喜忧参半.:)

    In my experience, @Mixin is sort of a mixed blessing. :)

    核心开发人员承认,它充满了难以解决"的错误.这并不是说它已经无用",远非如此.但是如果你有机会使用(或等待)groovy 2.3,那么你应该使用而是特征.

    It is, by the core developers admission, bug-ridden with "hard-to-solve" bugs. That's not to say that it's been "useless", far from it. But if you have the opportunity to use (or wait for) groovy 2.3, then you should use traits instead.

    AST 转换所做的只是将一个类中的方法添加到另一个类中.例如:

    What the AST transform does, is simply to add the methods from one class into another. For instance:

    class First {
        String hello(String name) { "Hello $name!" }
    }
    
    @Mixin(First)
    class Second {
        // more methods
    }
    
    assert new Second().hello('Vahid') == 'Hello Vahid!'
    

    总结:

    • 将一个类中的方法添加到另一个类中.
    • 在 groovy <2.3 中用于简单地将方法从一个类添加到另一个类
      • 不要添加到超级"类(至少,我遇到过问题)
      • Second 不是 First
      • 的实例
      • I.e. Second is not an instance of First
      • 钻石问题呢,即如果你在混合类中有方法签名相同?
      • what about the diamond problem, i.e. if you have methods in the mixed in classes with the same signature?

      运行时混合

      运行时 mixin 和 @Mixin 转换是完全不同的,它们解决不同的用例并被使用在完全不同的情况下.由于它们具有相同的名称,因此很容易将它们与另一个混淆,或者认为他们是一回事.然而,运行时 mixin 在 groovy 2.3 中被弃用.

      Runtime mixins and the @Mixin transform are quite different, they solve different use-cases and are used in totally different situations. Since they have the same name, it's easy to confuse one with the other, or to think that they are one and the same. Runtime mixins, however, are not deprecated in groovy 2.3.

      我倾向于将运行时 mixin 视为向现有类(例如 JDK 中的任何类)添加方法的方式.这是 Groovy 用来向 JDK 添加额外方法的机制.

      I tend to think about runtime mixins as the way to add methods to existing classes, such as any class in the JDK. It's the mechanism used by Groovy to add extra methods to the JDK.

      例子:

      class MyStringExtension {
          public static String hello(String self) {
              return "Hello $self!"
          }
      }
      
      String.mixin(MyStringExtension)
      
      assert "Vahid".hello() == 'Hello Vahid!'
      

      Groovy 还有一个不错的扩展模块 功能,您可以在其中不需要手动执行 mixin,而是只要在类路径中的正确位置找到模块描述符,groovy 就会为您执行此操作.

      Groovy also have a nice extension module feature, where you don't need to manually perform the mixin, instead groovy does it for you as long as it finds the module descriptor in the correct location in the classpath.

      总结:

      • 向任何现有类添加方法
        • JDK 中的任何类
        • 任何第 3 方课程
        • 或您自己的任何课程

        特征

        特征是 groovy 2.3 的新特性.

        Traits are new to groovy 2.3.

        我倾向于将这些特征视为熟悉的界面和类之间的某种东西.类似于轻量级"的东西班级.它们在文档中被称为具有默认实现和状态的接口".

        I tend to view these traits as something between the familiar interface and class. Something akin to a "light-weight" class. They are dubbed "interfaces with default implementations and state" in the documentation.

        Traits 类似于它们替换的 @Mixin 转换,但它们也更强大.首先,他们更加明确.trait 不能直接实例化,就像接口一样,它们需要实现班级.一个类可以实现许多特征.

        Traits are similar to the @Mixin transform that they replace, but they are also more powerful. For starters, they are much more well-defined. A trait cannot be instantiated directly, just like an interface, they need an implementing class. And a class may implement many traits.

        一个简单的例子:

        trait Name {
            abstract String name()
            String myNameIs() { "My name is ${name()}!" }
        }
        trait Age {
            int age() { 42 }
        }
        
        class Person implements Name, Age {
            String name() { 'Vahid' }
        }
        
        def p = new Person()
        assert p.myNameIs() == 'My name is Vahid!'
        assert p.age() == 42
        assert p instanceof Name
        assert p instanceof Age
        

        trait 和 @Mixin 的直接区别在于 trait 是语言关键字,而不是 AST 转换.此外,它可以包含需要由类实现的抽象方法.此外,一个类可以实现几个特点.实现 trait 的类是该 trait 的一个实例.

        The immediate difference between traits and @Mixin is that trait is a language keyword, not an AST transform. Further, it can contain abstract methods that needs to be implemented by the class. Further, a class can implement several traits. The class implementing a trait is an instance of that trait.

        总结:

        • 特征提供具有实现和状态的接口.
        • 一个类可以实现多个特征.
        • 通过 trait 实现的方法在 Java 中可见.
        • 兼容类型检查和静态编译.
        • Traits 可以实现接口.
        • 特征不能自行实例化.
        • 一个特征可以扩展另一个特征.
        • 钻石问题的处理是明确定义的.
        • 典型用法:
          • 为不同的类添加相似的特征.
            • (作为 AOP 的替代品)
            • Traits provide an interface with implementation and state.
            • A class can implement multiple traits.
            • Methods implemented by a trait are visible in Java.
            • Compatible with type checking and static compilation.
            • Traits can implement interfaces.
            • Traits can not be instantiated by themselves.
            • A trait can extend another trait.
            • Handling of the diamond problem is well-defined.
            • Typical usage:
              • add similar traits to different classes.
                • (as an alternative to AOP)

                这篇关于Groovy 中@Delegate、@Mixin 和 Traits 之间的区别?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆