80

Java の世界 (より正確には、複数の継承/ミックスインがない場合) の経験則は非常に単純です: 「クラスの継承よりもオブジェクトの構成を優先する」。

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

オブジェクト構成でも機能する場合に mixin を使用 (または悪用) する例をいくつか見てきましたが、どちらが優れているかは常にわかりません。それらを使用して非常に類似したことを達成できるように思えますが、いくつかの違いもあります。いくつかの例:

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

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

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

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

多くの場合、mixin の方が簡単 (および/または冗長度が低い) ように見えますが、"God クラス" や、artima の 2 つの記事: part 1part 2 (BTW it他の問題のほとんどは関連性がなく、scala にはそれほど深刻ではないように思えます)。

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

4

2 に答える 2

41

人々がミックスインで抱えている問題の多くは、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 

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

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

于 2010-08-06T19:30:20.807 に答える
11

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

  • 特性クラスには独立した存在はありません。

( Scala のプログラミング)

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

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

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

したがって、引き続き Programming Scala からのアドバイス:

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

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

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

于 2010-08-06T10:17:51.330 に答える