Doc Brownの答えは、デメテルの法則の古典的な教科書の実装を示しています-そして、そのように多数のメソッドを追加することの煩わしさ/組織化されたコードの膨張は、おそらく、私自身が含めたプログラマーが、たとえそうであっても、そうすることを気にしない理由です。
オブジェクトの階層を分離する別の方法があります。
メソッドやプロパティを介して、interface
型ではなく型を公開class
します。
元のポスター(OP)の場合、encoder->WaitEncoderFrame()
のIEncoderFrame
代わりにを返し、Frame
許可される操作を定義します。
解決策1
最も簡単なケースではFrame
、Encoder
クラスは両方とも制御下にIEncoderFrame
あり、Frameは既に公開されているメソッドのサブセットであり、Encoder
クラスは実際にそのオブジェクトに対して何をするかを気にしません。次に、実装は簡単です(c#のコード):
interface IEncoderFrame {
void DoOrGetSomething();
}
class Frame : IEncoderFrame {
// A method that already exists in Frame.
public void DoOrGetSomething() { ... }
}
class Encoder {
private Frame _frame;
public IEncoderFrame TheFrame { get { return _frame; } }
...
}
解決策2
Frame
定義があなたの制御下にない、またはIEncoderFrame
のメソッドをに追加するのが適切でない中間の場合、適切Frame
なソリューションはAdapterです。それがCandiedOrangeの答えが議論していることnew FrameHandler( frame )
です。重要:これを行う場合、クラスとしてではなくインターフェイスとして公開する方がより柔軟です。は知る必要がありますが、クライアントは知る必要があるだけです。または、私が名前を付けたように、-それがエンコーダのPOVから見た具体的なフレームであることを示すために:Encoder
class FrameHandler
interface IFrameHandler
interface IEncoderFrame
interface IEncoderFrame {
void DoOrGetSomething();
}
// Adapter pattern. Appropriate if no access needed to Encoder.
class EncoderFrameWrapper : IEncoderFrame {
Frame _frame;
public EncoderFrameWrapper( Frame frame ) {
_frame = frame;
}
public void DoOrGetSomething() {
_frame....;
}
}
class Encoder {
private Frame _frame;
// Adapter pattern. Appropriate if no access needed to Encoder.
public IEncoderFrame TheFrame { get { return new EncoderFrameWrapper( _frame ); } }
...
}
COST:毎回新しいオブジェクトEncoderFrameWrapperの割り当てとGC encoder.TheFrame
が呼び出されます。(そのラッパーをキャッシュすることはできますが、コードが追加されます。エンコーダーのフレームフィールドを新しいフレームに置き換えることができない場合にのみ、確実に簡単にコーディングできます。)
解決策3
より難しいケースでは、新しいラッパーはEncoder
との両方について知る必要がありFrame
ます。そのオブジェクト自体がLoDに違反することになります-エンコーダーとフレームの間の関係を操作することは、エンコーダーの責任であるべきであり、おそらく正しくなるのは苦痛です。その道を始めた場合に起こりうることは次のとおりです。
interface IEncoderFrame {
void DoOrGetSomething();
}
// *** You will end up regretting this. See next code snippet instead ***
class EncoderFrameWrapper : IEncoderFrame {
Encoder _owner;
Frame _frame;
public EncoderFrameWrapper( Encoder owner, Frame frame ) {
_owner = owner; _frame = frame;
}
public void DoOrGetSomething() {
_frame.DoOrGetSomething();
// Hmm, maybe this wrapper class should be nested inside Encoder...
_owner... some work inside owner; maybe should be owner-internal details ...
}
}
class Encoder {
private Frame _frame;
...
}
それはいです。ラッパーがその作成者/所有者(エンコーダー)の詳細に触れる必要がある場合、複雑度の低い実装があります。
interface IEncoderFrame {
void DoOrGetSomething();
}
class Encoder : IEncoderFrame {
private Frame _frame;
// HA! Client gets to think of this as "the frame object",
// but its really me, intercepting it.
public IEncoderFrame TheFrame { get { return this; } }
// This is the method that the LoD approach suggests writing,
// except that we are exposing it only when the instance is accessed as an IEncoderFrame,
// to avoid extending Encoder's already large API surface.
public void IEncoderFrame.DoOrGetSomething() {
_frame.DoOrGetSomething();
... make some change within current Encoder instance ...
}
...
}
確かに、ここにたどり着くことがわかっていれば、これをしないかもしれません。LoDメソッドを記述するだけで、それを行うことができます。インターフェイスを定義する必要はありません。一方、インターフェイスが関連するメソッドをまとめてラップしているのが気に入っています。私は、フレームのように感じるものに対して「フレームのような操作」を行うことがどのように感じるかが好きです。
最終コメント
これを考慮してください:の実装者Encoder
が公開Frame frame
がアーキテクチャ全体に適切であると感じた場合、または「LoDを実装するよりもはるかに簡単」だった場合、代わりに最初に示したスニペットを実行した方がはるかに安全でした-の限定サブセットを公開しますインターフェイスとしてのフレーム。 私の経験では、それはしばしば完全に実行可能なソリューションです。必要に応じて、インターフェイスにメソッドを追加するだけです。(フレームに必要なメソッドがあることを「知っている」、または追加するのが簡単で議論の余地のないシナリオについて話しています。各メソッドの「実装」作業は、インターフェイス定義に1行追加することです。)最悪の将来のシナリオでも、そのAPIを動作させ続けることができることを知っています-ここで、IEncoderFrame
Frame
Encoder
。
また、に追加IEncoderFrame
する権限がない場合Frame
、または必要なメソッドが一般Frame
クラスに適合せず、ソリューション2があなたに合わない場合、おそらく余分なオブジェクトの作成と破壊のために、ソリューション#3はEncoder
、LoDを達成するためののメソッドを整理するための単なる方法と見なすことができます。何十ものメソッドをただ通過するだけではありません。それらをインターフェイスでラップし、「明示的なインターフェイスの実装」(c#を使用している場合)を使用して、そのインターフェイスを介してオブジェクトが表示されたときにのみアクセスできるようにします。
私が強調したい別の点は、機能をインターフェイスとして公開するという決定が、上記の3つの状況すべてを処理したことです。最初は、IEncoderFrame
単にFrame
の機能のサブセットです。2番目IEncoderFrame
はアダプターです。3番目IEncoderFrame
は、Encoder
s機能へのパーティションです。これらの3つの状況の間でニーズが変わっても問題ありません。APIは同じままです。