2009-08-17 9 views
35

WPFアプリケーションでクラス階層に問題が発生しました。これは、2つの継承ツリーが合併している問題の1つで、継承を持たない継承をスムーズに行うための論理的な方法を見つけることはできません。この種のシステムをうまく動作させるための賢明なアイデアがあれば、それに従うことも、デバッグすることも不可能ではありません。.NETでの複数継承の良い選択肢は何ですか?

私は低レベルのエンジニアなので、最初の考えは常に「ああ、私はネイティブC++でこれらのクラスのいくつかを書いて、それらを外部から参照します! OOの楽しみ!

____________________________________  _____________________________________ 
| CustomizableObject     | | System.Windows.Controls.UserControl | 
|____________________________________| |_____________________________________| 
| string XAMLHeader()    |      ▲ 
| string XAMLFooter()    |◄--┐     | 
| CustomizableObject LoadObject() | \     | 
| <Possible other implementations> | \     | 
|____________________________________|  \     | 
     ▲      ▲   \     | 
     |      |   \    | 
     |      |    \    | 
_________________ ______________________ \ _____________________ 
| SpriteAnimation | | SpriteAnimationFrame | └---| CustomizableControl | 
|_________________| |______________________|  |_____________________| 
                 ▲    ▲ 
                 |    | 
                 |    | 
                ________ _____________ 
               | Sprite | | SpriteFrame | 
               |________| |_____________| 

問題はかなり明確である:分離あなたがマネージコントロールを継承する必要がある場合残念ながら、これは

は私が私の現在の投影クラス図のスニペットを表示することを許可する...助けにはなりませんCustomizableObjectとCustomizableControlオブジェクトツリーの両方に、UserControlをツリーの1つに挿入し、両方には挿入しません。

CustomizableObjectの実装を派生クラスに移行するのは現実的ではありません。実装はクラスによって異なるためです。さらに、複数回実装するのは本当に紛らわしいでしょう。だから私は本当にCustomizableObjectをインターフェイスにしたくありません。インターフェイスの解決策は私には分かりません。 (インターフェイスはを持っていません。は正直言って私にとって本当に意味がありました...)

私はもう一度言っています。これは本当のピクルスです。私は、インターフェイスを自分のオブジェクトツリーで動作させる方法についてもっと知りたいと思っています。私は、この単純なスプライトエンジンをWPFとC#を使って、何よりも強固な運動として作っています。これはC++で非常に簡単に解決できますが、管理対象環境でこれらの問題を解決する方法を理解する必要があります。

+19

+1これらすべてを描画するために: – Kamarey

+1

母、私の芸術作品が高く評価されてうれしいです。 – Giffyguy

+1

この質問はよくstackoverflows Q&A形式に収まりません。 「良い選択肢は何か」は、すでに正解が1つしかないことを前提としています。そこにはさまざまな方法や意見があります。 –

答えて

17

1つのアプローチは、インターフェイスを持つ拡張メソッドを使用して、System.Linqと同様に、「派生クラス」の実装を提供することです。照会可能:

interface ICustomizableObject 
{ 
    string SomeProperty { get; } 
} 

public static class CustomizableObject 
{ 
    public static string GetXamlHeader(this ICustomizableObject obj) 
    { 
     return DoSomethingWith(obj.SomeProperty); 
    } 

    // etc 
} 

public class CustomizableControl : System.Windows.Controls.UserControl, ICustomizableObject 
{ 
    public string SomeProperty { get { return "Whatever"; } } 
} 

用途:限り、あなたがのために使用してディレクティブを持っている(または同じ名前空間にある)あなたの拡張メソッドが定義されている名前空間として:私は

var cc = new CustomizableControl(); 
var header = cc.GetXamlHeader(); 
+0

あなたのための例。コンパイラがそれらを見つけることができる限り、拡張メソッドはインスタンスメソッドと同じように見えます。残念ながら、拡張プロパティはありません。したがって、メソッド命名規則に従うメソッドの名前を変更しました。 – dahlbyk

+4

また、MSDNのこの記事も参照してください。http://msdn.microsoft.com/en-us/vcsharp/bb625996.aspx – dahlbyk

+0

このソリューションは、この問題を完全に解決します。それは本当に狂っている。ここでのボーナスは、オブジェクトツリー内の実装を単一の場所に保持することです。ポリモーフィックまたはインターフェイスで展開するのではなく、WPFコントロールの考え方とうまく機能します。私は、この解決策を複数の継承/インターフェイスの問題を.NETで持っている人にお勧めします。 – Giffyguy

1

インターフェイスとオブジェクトの構成に頼らざるを得ないようです。

29

ここには2つのオプションがあります。インターフェイスを使用するか、または合成を使用します。正直なところ、インターフェイスは非常に強力で、この行を読んだ後に

インターフェイスソリューションは私には意味をなさない。 (インターフェイスは正直言って本当に私には分かりませんでした...)

私はあなたがそれらを正しく使う方法を学ぶべきだと思います。つまり、複数のクラスに必要なロジックがあるだけで、これらのクラスが同じ基本クラスから継承されない場合は、そのロジックをカプセル化するクラスを作成し、そのクラスのメンバー変数をクラスに追加しますそれはあなたに問題を与えている。このように、すべてのクラスにはロジックが含まれていますが、継承階層では別々のクラスにすることができます。クラスが共通インタフェースを実装する必要がある場合は、インタフェースを使用します。

+0

私はあなたがこれを説明する方法が本当に好きです。私はおそらく "構成"のルートを取っているようだ。それは面倒なことを複雑にしますが、私の実装を全面的に行うよりもそれほど難しくありません。 (母、私はとても頑固です...私は勇気を出してインターフェースを助けるために直接頼みますが、私は自分のプライドを飲み込めず、インターフェースの力についてのアドバイスを受け入れることはできません) – Giffyguy

7

​​はインターフェイスに作られるのを叫んでいます(コンクリートタイプはすべてオブジェクトに変換できるので、その名前の部分は冗長です)。あなたが実行している問題は、共有されるか、または実装によって少しだけ変化する基本的なロジックを保持する方法がわからないことです。このロジックをツリー自体に格納して、多態的に機能するようにしますその言葉?)。

これは、代理人によって実現できます。私はメンバーがあなたにトラブルを与えている正確にどのわからないんだけど、おそらくもっとこのような何か:

____________________________________  _____________________________________ 
| ICustomizable      | | System.Windows.Controls.UserControl | 
|         | |_____________________________________| 
| Func<string> XAMLHeader;   |      ▲ 
| Func<string> XAMLFooter   |◄--┐     | 
| ICustomizabl LoadObject() | \     | 
| <Possible other implementations> | \     | 
|____________________________________|  \     | 
     ▲      ▲   \     | 
     |      |   \    | 
     |      |    \    | 
_________________ ______________________ \ _____________________ 
| SpriteAnimation | | SpriteAnimationFrame | └---| CustomizableControl | 
|_________________| |______________________|  |_____________________| 
                 ▲    ▲ 
                 |    | 
                 |    | 
                ________ _____________ 
               | Sprite | | SpriteFrame | 
               |________| |_____________| 

また、あなたはおそらく、あなたが本当にあなたCustomizableObjectタイプでに属し感じること真に静的であり、いくつかのロジックを持っています。しかし、これはおそらく間違いです。特定の状況でその型を使用する意図で型を構築したとします。たとえば、コンテキストからは、これらのコントロールやアニメーションを作成してWindowsフォームで使用するように見えます。基本的なSystem.Windows.Formから継承する独自のフォームがあり、この新しいフォームタイプはICustomizableObjectとその使用方法を知っている必要があります。それがあなたの静的なロジックが行くところです。

これはやや厄介なようですが、プレゼンテーションエンジンを変更すると正確であることが証明されています。このコードをWPFまたはSilverlightに移植するとどうなりますか? Windowsフォームとは少し異なる実装コードを使用する必要がありますが、CustomizableControlの実装を変更する必要があります。しかし、あなたの静的ロジックは今、まさに正しい場所にあります。

最後に、使用しているLoadObject()メソッドは間違った場所のように私に目立っています。すべてのカスタマイズ可能なタイプに、自分自身をロード/構築する方法を知っている、呼び出すことのできるメソッドを提供したいと言っています。しかし、これは本当に異なるものです。 IConstructable<T>という名前の別のインターフェイスが必要で、そのICustomizableタイプが(IConstructable<ICustomizable>)を実装しているとします。

+0

+1あなたは非常に興味深い点を挙げています。私は本当に私のスプライトコントロールが自己完結しているのが好きです。これは結局のところWPFなので、WinFormsのように基底ウィンドウ型から継承するべきではないでしょうが、私のSpriteLayerコントロールやSpriteViewコントロールにこのソリューションを簡単に適用できます。 – Giffyguy

+1

私は、デリゲートのアプローチも本当に好きです。私は実際にデリゲートがこのためにどのように働くのか見てみるために、ある時間コーディングを費やしました。私はあなたの答えを受け入れることを計画していました。私はそれをコンパイルして実行しました。拡張メソッドがうまく機能しました。しかし、これは間違いなく確かに注目に値する素晴らしい解決策です。実際、デリゲートが拡張メソッドよりも優れた問題を解決するケースがたくさんあると確信しています。 – Giffyguy

1

このような場合はミックスインを使用します。 mixinsは、実行時にクラスに機能を追加するために、多くの言語で使用される強力なコンセプトです。 mixinsは多くの言語でよく知られています。リミックスフレームワークは、ミックスインテクノロジを.NETにもたらすフレームワークです。

アイデアは簡単です:ミックスインを表すクラス属性でクラスを飾ります。実行時にこの機能がクラスに追加されます。したがって、複数の継承をエミュレートできます。

問題を解決するには、CustomizableObjectをmixinにすることができます。これにはリミックスの枠組みが必要です。

[使用方法(CustomizableObjectMixin)] CustomizableControl:ユーザーコントロール

詳細はremix.codeplex.comをチェックしてみて下さい。

1

私は解決策の一つだと思います。

public interface IClassA 
{ 
    void Foo1(); 
    void Foo2(); 
} 

public interface IClassB 
{ 
    void Foo3(); 
    void Foo4(); 
} 

public class ClassA :IClassA 
{ 
    #region IClassA Members 

    public void Foo1() 
    { 
    } 

    public void Foo2() 
    { 
    } 

    #endregion 
} 

public class ClassB :IClassB 
{ 
    #region IClassB Members 

    public void Foo3() 
    { 
    } 

    public void Foo4() 
    { 
    } 

    #endregion 
} 

public class MultipleInheritance :IClassA, IClassB 
{ 
    private IClassA _classA; 
    private IClassB _classB; 

    public MultipleInheritance(IClassA classA, IClassB classB) 
    { 
     _classA = classA; 
     _classB = classB; 
    } 

    public void Foo1() 
    { 
     _classA.Foo1(); 
    } 

    public void Foo2() 
    { 
     _classA.Foo2(); 
     AddedBehavior1(); 
    } 

    public void Foo3() 
    { 
     _classB.Foo3(); 
     AddedBehavior2(); 
    } 

    public void Foo4() 
    { 
     _classB.Foo4(); 
    } 

    private void AddedBehavior1() 
    { 

    } 

    private void AddedBehavior2() 
    { 

    } 
} 

クラスMultipleInheritanceは、このオブジェクトの動作に影響を与えずに2つの異なるオブジェクトに新しい動作を追加します。 MultipleInheritanceは、渡されたオブジェクトと同じ動作を持ちます(両方の動作を継承します)。私の英語のためのソーリー。よろしく。

+0

私はそれをデコレータパターンとしては見ません。 –

+0

私はこれが有効であり、可能なアプローチであると思っています。私はあなたがなぜ落選したのか分かりません。明らかに、デコレータは既存のクラスを拡張するためのものであるため、一般的なアプローチではありません。デコレータ型を直接使用して、完全に新しいメソッドを追加することもできますが、私は_that_をデコレータパターンではないと見ることができますが、それは構成のバリエーションとして機能します。 – julealgon

関連する問題