【问题标题】:Difference between @Delegate, @Mixin and Traits in Groovy?Groovy 中@Delegate、@Mixin 和 Traits 之间的区别?
【发布时间】:2014-06-01 01:27:27
【问题描述】:

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

它们似乎都允许重用多个“类”行为。谢谢。 :-)

这个 SO 线程也很有帮助:Difference between @Delegate and @Mixin AST transformations in Groovy

【问题讨论】:

    标签: design-patterns groovy delegates mixins traits


    【解决方案1】:

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

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

    结论/典型用法:

    • @Delegate:用于添加委托类的所有功能,但仍避免与 实际执行。让你实现composition over inheritance
    • @Mixin:在 groovy 2.3 中已弃用。将一个或多个类中的方法添加到您的类中的简单方法。漏洞百出。
    • Runtime mixin:将一个或多个方法添加到 any 现有类中,例如JDK 或第 3 方库中的类。
    • 特征:groovy 2.3 中的新特性。向您的类添加一个或多个特征的明确定义的方法。替换@Mixin。唯一的 在 Java 类中可以看到添加的方法之一。

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

    @Delegate

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

    相反,您应该努力使用composition over inheritance

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

    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),所以它不会那么直截了当 使用继承来实现。

    总结:

    • 为委托对象中的所有公共方法提供默认实现。
      • 显式添加的具有相同签名的方法优先。
    • 隐式添加的方法在 Java 中可见。
    • 您可以将多个@Delegates 添加到一个类中。
      • 但如果您这样做,您应该考虑这是否真的可取。
      • diamond problem 怎么样,即如果您在委托中有多个具有相同签名的方法?
    • 具有委托的类(上例中的CountingList)不是委托类的实例。
      • CountingList 不是 LinkedList 的实例。
    • 用于通过继承避免紧密耦合。

    @Mixin

    由于即将推出的特征支持,@Mixin 转换将在 groovy 2.3 中弃用。这提供了一个 暗示所有可能与 @Mixin 相关的事情,都应该与特质相关。

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

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

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

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

    总结:

    • 将一个类中的方法添加到另一个类中。
    • 在 groovy
    • 不要添加到“超级”类(至少,我遇到过问题)
  • 漏洞百出
  • 从 groovy 2.3 中弃用
  • 隐式添加的方法在 Java 中可见。
  • 混入另一个类的类不是该其他类的实例
    • Second 不是 First 的实例
  • 您可以将多个班级混入另一个班级
    • diamond problem 怎么样,即,如果您在混合类中具有相同签名的方法?
  • 在 groovy 中用作将一个类的功能添加到另一个类的简单方法

    运行时混合

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

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

    例子:

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

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

    总结:

    • 向任何现有类添加方法
      • JDK 中的任何类
      • 任何第 3 方课程
      • 或您自己的任何课程
    • 覆盖具有相同签名的任何现有方法
    • 添加的方法在 Java 中可见
    • 通常用于使用新功能扩展现有/第 3 方类

    特征

    特征是 groovy 2.3 的新特性。

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

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

    一个简单的例子:

    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
    

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

    总结:

    • 特征提供具有实现和状态的接口。
    • 一个类可以实现多个特征。
    • 特征实现的方法在 Java 中是可见的
    • 兼容类型检查和静态编译。
    • 特征可以实现接口。
    • 特征不能自行实例化。
    • 一个特征可以扩展另一个特征。
    • diamond problem 的处理是明确定义的。
    • 典型用法:
      • 为不同的类添加相似的特征。
        • (作为 AOP 的替代品)
      • 从几个特征组成一个新类。
  • 【讨论】:

    • 您的回答很好,但请注意,这些特征与 @CompileStatic 等 AST 转换不正式兼容。 beta.groovy-lang.org/docs/groovy-2.3.0/html/documentation/…
    • 非常感谢这个解释清楚的答案!你推荐什么关于 Groovy 的书?
    • @AlexanderSuraphel 以我的经验,Groovy in Action 是一本很棒的书。它不仅深入地展示了 Groovy,而且还很好地讨论了一般编程概念以及 Groovy 如何帮助而不是替代 Java,以提供极其强大的开​​发环境。
    • Steinar,你提到的“钻石问题”是如何处理的?
    • @AlexanderSuraphel:我猜迟到总比没有好,请参阅this answer,了解如何处理钻石问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-06-27
    • 1970-01-01
    • 2015-04-13
    • 2011-07-13
    • 1970-01-01
    • 1970-01-01
    • 2010-10-17
    相关资源
    最近更新 更多