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

Vah*_*deh 56 groovy delegates design-patterns traits mixins

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

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

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

Ste*_*nar 116

我同意,他们似乎都允许重复使用多个"类"的行为.但是,存在差异,理解这些可能有助于您做出决定.

在提供每个功能的简要摘要/重点和适当用法的示例之前,让我们总结每个功能的结论.

结论/典型用法:

  • @Delegate:用于添加委托类的所有功能,但仍然避免与实际实现紧密耦合.让我们实现构图而不是继承.
  • @Mixin:用groovy 2.3弃用.将一个或多个类中的方法添加到类中的简单方法.充满错误的.
  • 运行时mixin:将一个或多个方法添加到任何现有类中,例如JDK中的类或第三方库.
  • 特点:groovy 2.3中的新功能.定义为您的类添加一个或多个特征的方法.取代@Mixin.其中唯一一个添加方法在Java类中可见.

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

@代表

在许多情况下,继承被过度使用.也就是说,它经常被不正确地使用.Java中的经典示例是扩展输入流,读取器或集合类.对于大多数这些,使用继承与实现过于紧密耦合.也就是说,编写实际的实现,以便其中一个公共方法实际使用另一个.如果你同时覆盖两个,并且你打电话super,那么你可能会得到不必要的副作用.如果实现在更高版本中更改,那么您还必须更新对它的处理.

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

例如,计算添加到列表中的元素的计数列表:

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
}
Run Code Online (Sandbox Code Playgroud)

在此示例中,@Delegate删除了您希望"按原样"保留的所有公共方法的所有繁琐的样板代码,即添加了简单地将调用转发到基础列表的方法.此外,CountingList它与实现分离,因此您无需关心是否通过调用另一个来实现这些方法之一.在上面的例子中,实际情况就是这样,因为 LinkedList.add(Collection)调用LinkedList.add(int, Collection),所以使用继承实现它并不是那么简单.

摘要:

  • 为委派对象中的所有公共方法提供默认实现.
    • 具有明确添加的相同签名的方法优先.
  • 在Java 中看不到隐式添加的方法.
  • 您可以将几个@Delegates 添加到一个类中.
    • 但如果你这样做,你应该考虑这是否真的可取.
    • 钻石问题怎么样,即如果代表中有多个方法具有相同的签名?
  • 具有委托的类(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!'
Run Code Online (Sandbox Code Playgroud)

摘要:

  • 将方法从一个类添加到另一个类.
  • 在groovy <2.3中使用,可以简单地将方法从一个类添加到另一个类
    • 不要添加到"超级"类(至少,我遇到了问题)
  • 充满错误的
  • 从groovy 2.3中弃用
  • 在Java 中看不到隐式添加的方法.
  • 获取另一个类的类不是该类的实例
    • Second不是一个实例First
  • 您可以将多个类混合到另一个类中
    • 钻石问题怎么样,即如果你在具有相同签名的混合类中有方法?
  • 用作在groovy <2.3中将一个类的功能添加到另一个类的简单方法

运行时混合

运行时混合和@Mixin变换是完全不同的,它们解决了不同的用例,并且在完全不同的情况下使用.由于它们具有相同的名称,因此很容易将一个与另一个混淆,或者认为它们是同一个.但是,在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!'
Run Code Online (Sandbox Code Playgroud)

Groovy还有一个很好的扩展模块功能,你不需要手动执行mixin,只要它在类路径中的正确位置找到模块描述符,groovy就会为你做.

摘要:

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

性状

特征是groovy 2.3的新特征.

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

特征与@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
Run Code Online (Sandbox Code Playgroud)

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

摘要:

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

  • 非常感谢这个解释得很好的答案!你推荐什么关于Groovy的书? (2认同)