design-patterns - Groovy 中@Delegate、@Mixin 和 Traits 的区别?

标签 design-patterns groovy delegates mixins traits

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

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

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

最佳答案

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

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

结论/典型用法:

  • @代表 : 用于添加委托(delegate)类的所有功能,但仍然避免与委托(delegate)类的紧密耦合
    实际执行。让你成就composition over inheritance .
  • @Mixin :已弃用 groovy 2.3。将一个或多个类中的方法添加到您的类中的简单方法。 bug 缠身。
  • 运行时混入 :将一个或多个方法添加到任何现有类中,例如JDK 或 3rd 方库中的类。
  • 性状 :groovy 2.3 中的新功能。向类中添加一个或多个特征的明确定义的方法。替换@Mixin。唯一的
    其中之一是在 Java 类中可以看到添加的方法。

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

    @代表

    在许多情况下,继承被过度使用。也就是说,它经常被不当使用。 Java 中的经典示例是
    扩展输入流、读取器或集合类.. 对于大多数这些,使用继承太
    与实现紧密结合。也就是说,实际的实现是这样编写的
    公共(public)方法实际上使用另一个。如果您覆盖两者,并调用 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删除所有公共(public)方法的所有繁琐的样板代码
    想要保持“原样”,即添加了简单地将调用转发到基础列表的方法。此外,CountingList与实现分开,这样您就不必关心其中之一
    方法是通过调用另一个来实现的。在上面的例子中,情况确实如此,因为LinkedList.add(Collection)电话LinkedList.add(int, Collection) ,所以它不会那么简单
    使用继承来实现。

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

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

    根据我的经验,@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 <2.3 中用于简单地将方法从一个类添加到另一个类
  • 不要添加到“ super ”类(至少,我遇到了问题)
  • 漏洞百出
  • 从 groovy 2.3 弃用
  • 隐式添加的方法在 Java 中不可见。
  • 混入另一个类的类不是该另一个类的实例
  • IE。 Second不是 First 的实例
  • 您可以将几个类(class)混合到另一个类(class)中
  • diamond problem 怎么样? ,即如果您在具有相同签名的混合类中有方法?
  • 在 groovy <2.3
  • 中用作将一个类的功能添加到另一个类的简单方法

    运行时混入

    运行时 mixins 和 @Mixin变换完全不同,它们解决不同的用例并被使用
    在完全不同的情况下。由于它们具有相同的名称,因此很容易将一个与另一个混淆,或者
    认为他们是一回事。然而,运行时 mixins 在 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 方类(class)
  • 或您自己的任何类(class)
  • 覆盖具有相同签名的任何现有方法
  • 添加的方法在 Java 中不可见
  • 通常用于使用新功能扩展现有/第 3 方类

  • 性状

    特性是 groovy 2.3 的新特性。

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

    性状类似于 @Mixin转换它们取代,但它们也更强大。首先,他们
    更明确。 trait 不能直接实例化,就像接口(interface)一样,它们需要一个实现
    类(class)。一个类可以实现许多特征。

    一个简单的例子:
    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 的一个实例。

    概括:
  • Traits 提供了一个带有实现和状态的接口(interface)。
  • 一个类可以实现多个特征。
  • 由 trait 实现的方法在 Java 中可见。
  • 兼容类型检查和静态编译。
  • Traits 可以实现接口(interface)。
  • 特征不能自己实例化。
  • 一个特质可以扩展另一个特质。
  • diamond problem的处理是明确定义的。
  • 典型用法:
  • 将相似的特征添加到不同的类。
  • (作为 AOP 的替代)
  • 从几个特征组成一个新类。
  • 关于design-patterns - Groovy 中@Delegate、@Mixin 和 Traits 的区别?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23121890/

    相关文章:

    java - 在下面的示例中使用抽象工厂而不是工厂方法有什么真正的好处?

    java - grails 应用程序中的 "Too many open files"- 如何正确关闭打开的文件和流?

    grails - 如何重新启动Grails Web应用程序

    c# - 在 C# 中将 Action 委托(delegate)作为参数传递

    c# - 逆变参数?

    c# - 调用 ASP.NET 用户控件事件时调用页面事件

    c# - 存储库模式逐步解释

    php - 有什么方法可以限制一个类只创建 2 个对象

    java - 可插入的错误处理策略

    git - Jenkins 多分支 : change Job description from Groovy