web-dev-qa-db-ja.com

ミックスインとコンポジションの比較scala

Java world(より正確には、多重継承/ミックスインがない場合)では、経験則は非常に単純です:「クラス継承よりもオブジェクトコンポジションを優先する」。

特にscalaでミックスインも検討した場合、それがどのように変更されるかを知りたいですか?
ミックスインは多重継承の方法と見なされますか、それともより多くのクラス構成と見なされますか?
「クラス構成よりもオブジェクト構成を優先する」(またはその逆)ガイドラインもありますか?

オブジェクトコンポジションでも機能する可能性があるときに、人々がミックスインを使用(または乱用)する例をかなり見ましたが、どちらが優れているかは必ずしもわかりません。あなたはそれらで非常に似たようなことを達成できるように私には思えますが、いくつかの違いもあります、いくつかの例:

  • 可視性-ミックスインを使用すると、すべてがパブリックAPIの一部になりますが、コンポジションの場合はそうではありません。
  • 冗長性-ほとんどの場合、ミックスインは冗長性が低く、少し使いやすいですが、常にそうであるとは限りません(たとえば、複雑な階層で自己型も使用する場合)

簡単な答えは「状況によって異なります」ですが、これまたはそれが優れている場合は、おそらくいくつかの典型的な状況があります。

これまでに思いついたガイドラインのいくつかの例(2つの特性AとBがあり、AがBのいくつかの方法を使用したいと仮定):

  • AのAPIをBのメソッドで拡張したい場合は、ミックスイン、それ以外の場合は合成。しかし、私が作成しているクラス/インスタンスがパブリックAPIの一部でない場合は役に立ちません。
  • ミックスインを必要とするいくつかのパターンを使用したい場合(例: Stackable Trait Pattern )、それは簡単な決定です。
  • 循環依存関係がある場合は、自己型とのミックスインが役立ちます。 (私はこの状況を避けようとしますが、それは必ずしも簡単ではありません)
  • 動的な実行時の決定が必要な場合は、コンポジションを実行してからオブジェクトコンポジションを実行します。

多くの場合、ミックスインの方が簡単(および/または冗長性が低い)のように見えますが、「神のクラス」や2つのartimaの記事で説明されているその他の落とし穴もあると確信しています。 パート1 =、 パート2 (ところで、他の問題のほとんどは、scalaには関係がない/それほど深刻ではないようです)。

このようなヒントは他にもありますか?

77
Sandor Murakozi

人々がミックスインで抱える問題の多くは、クラス定義に抽象特性のみをミックスインし、オブジェクトのインスタンス化で対応する具象特性をミックスインする場合、Scala)で回避できます。時間。例えば

trait Locking{
   // abstract locking trait, many possible definitions
   protected def lock(body: =>A):A
}

class MyService{
   this:Locking =>
}

//For this time, we'll use a Java.util.concurrent lock
val myService:MyService = new MyService with JDK15Locking 

この構成には、推奨するものがいくつかあります。まず、特性機能のさまざまな組み合わせが必要になるため、クラスが爆発的に増加するのを防ぎます。次に、モックオブジェクトと同様に、「何もしない」具体的な特性を作成して組み合わせることができるため、簡単にテストできます。最後に、使用されているロック特性を完全に隠し、そのロックが行われている場合でも、サービスの利用者から隠しました。

ミックスインの主張されている欠点のほとんどを乗り越えてきたので、ミックスインと構成の間のトレードオフがまだ残っています。私自身は、通常、仮想のデリゲートオブジェクトが含まれているオブジェクトによって完全にカプセル化されるかどうか、または共有されて独自のライフサイクルを持つ可能性があるかどうかに基づいて決定します。ロックは、完全にカプセル化されたデリゲートの良い例です。クラスがロックオブジェクトを使用してその内部状態への同時アクセスを管理する場合、そのロックは含まれているオブジェクトによって完全に制御され、クラスのパブリックインターフェイスの一部としてアドバタイズされません。このように完全にカプセル化された機能については、ミックスインを使用します。データソースのように共有されるものには、合成を使用します。

39
Dave Griffith

あなたが言及していない他の違い:

  • トレイトクラスには独立した存在はありません。

プログラミングScala

特定のトレイトが他のクラスの親として最も頻繁に使用され、子クラスが親トレイトとして動作することがわかった場合は、代わりにそのトレイトをクラスとして定義して、この論理関係をより明確にすることを検討してください。
(前者はリスコフの置換原則に基づく継承のより正確な定義であるため、is aではなくbehaves asと言いましたを参照] Martin2003]、例えば。)

[Martin2003]:Robert C. Martin、アジャイルソフトウェア開発:原則、パターン、および実践、Prentice-Hall、2003

  • ミックスイン(trait)にはコンストラクターパラメーターがありません。

したがって、 アドバイス、まだプログラミングScalaから

適切なデフォルト値に初期化できない特性の具体的なフィールドは避けてください。
代わりに抽象フィールドを使用するか、コンストラクターを使用してトレイトをクラスに変換します
もちろん、ステートレス特性には初期化に関する問題はありません。

優れたオブジェクト指向設計の一般原則は、構築プロセスが終了した瞬間から、インスタンスが常に既知の有効な状態にある必要があることです。

オブジェクトの--- [initial stateに関する最後の部分は、クラス(およびクラス構成)と特性のどちらを決定するのに役立つことがよくあります。 (およびミックスイン)特定の概念。

10
VonC