Difference between @Delegate, @Mixin and Groovy traits?

Can someone explain when I want to use Groovy Traits vs Mixins (@Mixin) vs delegates (@Delegate)? Perhaps some trade-offs and design issues will help.

All of them seem to allow the reuse of several "classes" of behavior. Thank you. :-)

This SO thread was also useful: The difference between the @Delegate and @Mixin AST conversions in Groovy

+45
design-patterns mixins traits delegates groovy
Apr 16 '14 at 23:17
source share
1 answer

I agree, all of them seem to allow the reuse of several "classes" of behavior. However, there are differences, and understanding this is likely to help your decision.

Before providing a brief overview / highlighting of each function and examples of suitable uses, we simply summarize each of them.

Conclusion / typical use:

  • @Delegate . Used to add all the functionality of the delegate class, but to avoid tight communication with the actual implementation. Let you achieve composition over inheritance .
  • @Mixin : deprecated with groovy 2.3. An easy way to add methods from one or more classes to your class. Error covered.
  • Mixing runtime . Add one or more methods to any existing class, for example. class in JDK or third-party library.
  • Features : new in groovy 2.3. A well-defined way to add one or more attributes to your class. Replaces @Mixin. The only one where added methods are visible in Java classes.

And now let's look at each of them in a bit more detail.

@Delegate

Inheritance is used in many cases. That is, it is often misused. Classic Java Examples: Extending Input Streams, Readers, or Collection Classes For most of them, using inheritance is too closely related to implementation. That is, the actual implementation is written so that one of the social methods actually use the other. If you override both and you call super , then you may get unwanted side effects. If the implementation changes in a later version, you will have to update your processing of this as well.

Instead, you should aim to use composition over inheritance .

An example is a count list that counts the items added to the list:

 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 } 

In this example, @Delegate removes all the tedious boiler room code for all public methods that you want to leave as-is, i.e. Added methods that simply redirect the call to the base list. In addition, the CountingList is separate from the implementation, so you do not need to worry about whether one of these methods is implemented by calling the other. In the above example, this is true since LinkedList.add(Collection) calls LinkedList.add(int, Collection) , so it is not so straightforward to implement using inheritance.

Summary:

  • Provides standard implementations for all public methods in a delegated object.
    • Signed methods that are explicitly added take precedence.
  • Implicitly added methods are not visible in Java.
  • You can add multiple @Delegate to one class.
  • The delegate class ( CountingList in the example above) are not instances of the delegate class.
    • those. CountingList not an instance of LinkedList .
  • Use to avoid tight communication through inheritance.

@Mixin

The @Mixin will be deprecated with groovy 2.3, due to support for upcoming features. This ensures that everything that is possible to do with @Mixin should be possible to do with traits instead.

In my experience, @Mixin is a kind of mixed blessing. :)

This, according to the developers of the main developers, is associated with errors that are "difficult to solve". This does not mean that it was "worthless", far from it. But if you have the opportunity to use (or wait) groovy 2.3, then you should use instead.

What AST transformation does is simply add methods from one class to another. For example:

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

Summary:

  • Adds methods from one class to another.
  • Use in groovy <2.3 to easily add methods from one class to another
    • don't add super to classes (at least I had problems with this)
  • Fixed bug covered
  • Deprecated of groovy 2.3
  • Implicitly added methods are not visible in Java.
  • A class that gets another class mixed is not an instance of this other class
    • those. Second not an instance of First
  • You can mix multiple classes into one class.
  • Use as an easy way to add the functionality of one class to another in groovy <2.3

Runtime Mixing

Runtime mixing and @Mixin conversion @Mixin completely different; they solve different use cases and are used in completely different situations. Since they have the same name, it is easy to confuse one with the other or think that they are one and the same. However, mixing in the runtime is not outdated in groovy 2.3.

I tend to think of runtime mixins as a way to add methods to existing classes, such as any class in the JDK. This is the mechanism used by groovy to add additional methods to the JDK.

Example:

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

Groovy also has a nice extension module where you don't need to manually mix, instead, groovy does this for you until it finds the module handle in the right place on the class path.

Summary:

  • Add methods to any existing class
    • any classes in jdk
    • any third-party classes
    • or any of your own classes
  • Overrides any existing method with the same signature
  • Added methods are not visible in Java
  • Commonly used to extend existing / third-party classes with new features

Traits

Traits are new for groovy 2.3.

I try to consider these features as a cross between a familiar interface and a class. Something like a "light weight" class. In the documentation, they are duplicated "interfaces with implementation and default state."

The traits are similar to the @Mixin transformation, which they replace, but they are also more powerful. First, they are much more clearly defined. It is impossible to instantiate directly as an interface; they need an implementation of the class. And a class can implement many features.

A simple example:

 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 

The immediate difference between the traits and @Mixin is that trait is a language keyword, not an AST conversion. In addition, it may contain abstract methods that must be executed by the class. In addition, a class can implement several features. A class that implements a feature is an instance of this feature.

Summary:

  • Traits provide an interface with implementation and state.
  • A class can implement several characteristics.
  • The methods implemented by the tag are visible in Java.
  • Compatible with type checking and static compilation.
  • Traits can implement interfaces.
  • Traits cannot be created on their own.
  • A trait may extend another trait.
  • The solution to the problem is clearly defined.
  • Typical Usage:
    • add similar features to different classes.
      • (as an alternative to AOP)
    • Create a new class from several characteristics.
+103
Apr 17 '14 at 5:01
source share



All Articles