请问何时应该使用 Groovy Traits,而不是 Mixins (@Mixin) 和 Delegates (@Delegate)?也许一些权衡和设计方面的考虑会有所帮助。
它们都似乎允许重用多个“类”的行为。谢谢。:-)
这个 SO 主题也很有帮助:Difference between @Delegate and @Mixin AST transformations in Groovy
请问何时应该使用 Groovy Traits,而不是 Mixins (@Mixin) 和 Delegates (@Delegate)?也许一些权衡和设计方面的考虑会有所帮助。
它们都似乎允许重用多个“类”的行为。谢谢。:-)
这个 SO 主题也很有帮助:Difference between @Delegate and @Mixin AST transformations in Groovy
@Delegate
继承在很多情况下被过度使用,也就是说经常被错误地使用。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
}
@Delegate
可以消除所有公共方法的样板代码,使你可以保留那些想要“原样保留”的方法,即添加一些简单的调用底层列表的方法。此外,CountingList
与实现分离,因此您不必关心其中一个方法是通过调用另一个方法来实现的。在上面的例子中,实际上就是这种情况,因为LinkedList.add(Collection)
调用了 LinkedList.add(int, Collection)
,所以使用继承实现并不是那么直观。@Delegate
,但如果这样做,您应该考虑是否真的需要。CountingList
)不是代理类的实例。@Mixin
由于即将到来的特征支持,@Mixin
转换将在groovy 2.3中被弃用。这提供了一种暗示,即使用特征应该能够完成使用@Mixin
所能完成的一切。
@Mixin
有点双刃剑。:) 核心开发人员承认它存在着“难以解决”的错误。这并不是说它是“无用的”,远非如此。但如果你有机会使用(或等待)Groovy 2.3,则应该使用traits代替。AST转换所做的就是将一个类的方法添加到另一个类中。例如:class First {
String hello(String name) { "Hello $name!" }
}
@Mixin(First)
class Second {
// more methods
}
assert new Second().hello('Vahid') == 'Hello Vahid!'
简介:
Second
不是First
的实例运行时混入
运行时混入和@Mixin
转换是完全不同的,它们解决不同的用例并在完全不同的情况下使用。由于它们具有相同的名称,很容易将一个与另一个混淆,或者认为它们是一样的。然而,在Groovy 2.3中,运行时混合并没有被弃用。class MyStringExtension {
public static String hello(String self) {
return "Hello $self!"
}
}
String.mixin(MyStringExtension)
assert "Vahid".hello() == 'Hello Vahid!'
@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
trait
是一个语言关键字,而不是AST转换。此外,它可以包含需要被类实现的抽象方法。此外,一个类可以实现多个traits。实现trait的类是该trait的实例。