@Delegate,@ Mixin和Groovy中的Traits之间的区别?

时间:2014-04-16 23:17:51

标签: design-patterns groovy delegates mixins traits

有人会解释我何时想要使用Groovy Traits与Mixins(@Mixin)对阵代表(@Delegate)?也许一些权衡和设计问题会有所帮助。

他们似乎都允许重复使用多个"类"行为谢谢。 : - )

此SO主题也很有用:Difference between @Delegate and @Mixin AST transformations in Groovy

1 个答案:

答案 0 :(得分:114)

我同意,他们似乎都允许重复使用多个"类"行为但是,有一些不同之处 了解这些可能有助于您的决定。

在提供每个功能的简要摘要/重点和适当的示例之前 用法,我们只是总结一下每个人的结论。

结论/典型用法:

  • @Delegate :用于添加委托类的所有功能,但仍然避免紧密耦合到 实际执行。让我们实现composition over inheritance
  • @Mixin :使用groovy 2.3弃用。将一个或多个类中的方法添加到类中的简单方法。充满错误的。
  • 运行时mixin :将一个或多个方法添加到任何现有类中,例如JDK中的一个类或第三方库。
  • 特征: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删除了您所有公共方法的所有繁琐的样板代码 想要离开&#34; as-is&#34;,即添加简单地将呼叫转发到基础列表的方法。此外, CountingList与实施分开,因此您无需关心其中一项 方法是通过调用另一个来实现的。在上面的例子中,实际情况就是这样,因为 LinkedList.add(Collection)调用LinkedList.add(int, Collection),因此不会那么直截了当 实现继承。

要点:

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

<强> @Mixin

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

根据我的经验,@Mixin是一种喜忧参半的祝福。 :)

由核心开发人员承认,错误地带着&#34;难以解决的问题&#34;错误。这并不是说它已经存在 &#34;无用&#34;,远离它。但如果您有机会使用(或等待)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&lt; 2.3中使用,可以简单地将方法从一个类添加到另一个类
    • 不要加入&#34; super&#34;课程(至少,我有问题)
  • 充满错误的
  • 从groovy 2.3
  • 弃用
  • 隐式添加的方法在Java中可见。
  • 混合使用另一个类的类不是其他类的实例
    • 即。 Second不是First
    • 的实例
  • 您可以将多个班级混合到另一个班级
    • diamond problem怎么样,即如果你在具有相同签名的混合类中有方法?
  • 用作在groovy&lt; 2.3
  • 中将一个类的功能添加到另一个类的简单方法

运行时mixin

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

我倾向于将运行时mixins视为向现有类添加方法的方法,例如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中的任何类
    • 任何第三方课程
    • 或您自己的任何课程
  • 使用相同签名覆盖任何现有方法
  • 在Java
  • 中添加 的方法
  • 通常用于使用新功能扩展现有/第三方类

<强>性状

特质是groovy 2.3的新功能。

我倾向于将这些特征视为熟悉的界面和类之间的东西。类似于轻盈的东西&#34; 类。它们被称为&#34;接口,默认实现和状态&#34;在文档中。

Traits与它们替换的@Mixin变换类似,但它们也更强大。对于初学者来说,他们 更加明确。特征不能直接实例化,就像接口一样,它们需要实现 类。一个班级可以实现许多特征。

一个简单的例子:

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

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

要点:

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