web-dev-qa-db-ja.com

Groovyの@ Delegate、@ Mixin、およびTraitsの違いは?

Groovy Traits vs. Mixins(@Mixin)vs. Delegates(@Delegate)を使用するタイミングを誰かが説明しますか?いくつかのトレードオフと設計上の懸念が役立つかもしれません。

それらはすべて、動作の複数の「クラス」を再利用できるように思われます。ありがとう。 :-)

このSOスレッドも参考になりました: @ Delegateと@Mixinの違いAST Groovyの変換

55

私は同意しますが、それらはすべて、複数の「クラス」の振る舞いを再利用できるようです。ただし、違いがあり、これらを理解することがおそらくあなたの決定に役立ちます。

各機能の簡単な要約/ハイライトと適切な使用例を提供する前に、それぞれの結論について要約しましょう。

結論/典型的な使用法:

  • @ Delegate:デリゲートクラスのすべての機能を追加するために使用しますが、実際の実装への密結合は避けます。 composition over inheritance を達成しましょう。
  • @ Mixin:groovy 2.3では非推奨です。 1つ以上のクラスのメソッドをクラスに追加する簡単な方法。バグに乗った。
  • ランタイムミックスイン:1つ以上のメソッドをany既存のクラスに追加します。 JDKまたはサードパーティライブラリのクラス。
  • Traits:groovy 2.3の新機能。クラスに1つ以上の特性を追加する明確な方法。 @Mixinを置き換えます。これらのメソッドのうち、追加されたメソッドはJavaクラスでのみ表示されます。

それでは、これらのそれぞれについてもう少し詳しく見ていきましょう。

@デリゲート

多くの場合、継承は使いすぎです。つまり、しばしば不適切に使用されます。 Javaの古典的な例は、入力ストリーム、リーダー、またはコレクションクラスを拡張しています。これらのほとんどについて、継承の使用は実装と密接に結びついています。パブリックメソッドの1つは実際に別のメソッドを使用します。両方をオーバーライドし、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_は、「そのまま」残したいすべてのパブリックメソッドの退屈なボイラープレートコードをすべて削除します。つまり、呼び出しを基になるリストに単に転送するメソッドが追加されます。さらに、CountingListは実装から分離されているため、これらのメソッドの一方が他方を呼び出して実装されているかどうかを気にする必要はありません。上記の例では、LinkedList.add(Collection)LinkedList.add(int, Collection)を呼び出すため、実際にそうなります。したがって、継承を使用して実装するのは簡単ではありません。

概要:

  • 委任されたオブジェクトのすべてのパブリックメソッドのデフォルト実装を提供します。
    • 明示的に追加された同じシグネチャを持つメソッドが優先されます。
  • 暗黙的に追加されたメソッドは、Javaではnot表示されません。
  • 1つのクラスに複数の_@Delegate_ sを追加できます。
    • しかし、そうする場合、それが本当に望ましいかどうかを検討する必要があります。
    • ダイアモンドの問題 、つまり、デリゲートに同じ署名を持つ複数のメソッドがある場合はどうですか?
  • デリゲートを持つクラス(上記の例ではCountingList)は、デリゲートクラスのインスタンスではありません。
    • つまりCountingListLinkedListのインスタンスではありません。
  • 継承による密結合を回避するために使用します。

@ Mixin

_@Mixin_変換は、今後の特性のサポートにより、groovy 2.3で非推奨になります。これは、_@Mixin_でできることはすべて、代わりにtraitでできるべきであるというヒントを提供します。

私の経験では、_@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!'
_

概要:

  • あるクラスから別のクラスにメソッドを追加します。
  • あるクラスから別のクラスにメソッドを簡単に追加するには、groovy <2.3で使用します
    • 「スーパー」クラスに追加しないでください(少なくとも、私は問題がありました)
  • バグが多い
  • Groovy 2.3から非推奨
  • 暗黙的に追加されたメソッドは、Javaではnot表示されません。
  • 別のクラスが混在するクラスは、その別のクラスのインスタンスではありません
    • つまりSecondFirstのインスタンスではありません
  • 複数のクラスを別のクラスに混在させることができます
    • ダイヤモンドの問題 についてはどうですか?つまり、同じシグネチャを持つクラスが混在しているメソッドがある場合はどうですか?
  • Groovy <2.3で、あるクラスの機能を別のクラスに追加する簡単な方法として使用する

ランタイムミックスイン

ランタイムミックスインと_@Mixin_トランスフォームはまったく異なり、異なるユースケースを解決し、まったく異なる状況で使用されます。それらは同じ名前を持っているので、一方を他方と混同したり、それらが同一であると考えるのは簡単です。ただし、ランタイムミックスインは、groovy 2.3で非推奨ではありません。

JDKのクラスなど、既存のクラスにメソッドを追加する方法として、ランタイムミックスインを考える傾向があります。 GroovyがJDKに追加のメソッドを追加するために使用するメカニズムです。

例:

_class MyStringExtension {
    public static String hello(String self) {
        return "Hello $self!"
    }
}

String.mixin(MyStringExtension)

assert "Vahid".hello() == 'Hello Vahid!'
_

Groovyには、Mixinを手動で実行する必要のないNice extension module 機能もあります。代わりに、groovyは、クラスパス内の正しい場所でモジュール記述子を検出する限り、それを行います。

概要:

  • 既存のクラスにメソッドを追加します
    • jDKのすべてのクラス
    • サードパーティクラス
    • または独自のクラスのいずれか
  • 同じシグネチャを持つ既存のメソッドをオーバーライドします
  • 追加されたメソッドはnotJavaでは表示されません
  • 通常、既存のサードパーティクラスを新しい機能で拡張するために使用されます

特徴

特性は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
_

特性と@Mixinの直接の違いは、traitは言語キーワードであり、AST変換ではありません。さらに、クラスによって実装される必要がある抽象メソッドを含むことができることです。さらに、クラスはいくつかの特性を実装できます特性を実装するクラスは、その特性のインスタンスです。

概要:

  • 特性は、実装と状態を備えたインターフェースを提供します。
  • クラスは複数の特性を実装できます。
  • 特性によって実装されるメソッドは、Javaで表示可能です
  • 型チェックおよび静的コンパイルと互換性があります。
  • 特性はインターフェースを実装できます。
  • 特性はそれ自体ではインスタンス化できません。
  • 特性は別の特性を拡張できます。
  • diamond problem の処理は明確に定義されています。
  • 典型的な使用法:
    • 同様の特性を異なるクラスに追加します。
      • (AOPの代替として)
    • いくつかの特性から新しいクラスを作成します。
115
Steinar