37

WPF アプリケーションで、クラス階層に少し問題が発生しました。これは、2 つの継承ツリーがマージされている問題の 1 つであり、多重継承なしでは継承をスムーズに機能させるための論理的な方法を見つけることができません。追跡やデバッグを不可能にすることなく、この種のシステムを機能させるための素晴らしいアイデアを誰かが持っているかどうか疑問に思っています。

私は低レベルのエンジニアなので、最初に考えるのはいつも「ああ、これらのクラスのいくつかをネイティブ C++ で記述して、外部から参照するだけだ!そうすれば、昔ながらの OO のすべてを楽しむことができる!」ということです。残念ながら、管理されたコントロールから継承する必要がある場合、これは役に立ちません...

現在投影されているクラス図のスニペットを表示させてください。

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

問題は明らかです。CustomizableObject と CustomizableControl のオブジェクト ツリーを分離し、UserControl をツリーの両方ではなく一方に挿入します。

CustomizableObject の実装をその派生クラスに移動することは、実際には意味がありません。実装はクラスによって異なるわけではないからです。さらに、複数回実装すると非常に混乱します。したがって、私は本当に CustomizableObject をインターフェイスにしたくありません。インターフェイス ソリューションは、私には意味がありません。(正直に言うと、インターフェースは私にはあまり意味 がありませんでした...)

もう一度言いますが、誰か素晴らしいアイデアをお持ちですか? これは本物のピクルスです。オブジェクト ツリーに対してではなく、オブジェクト ツリーと共にインターフェイスを機能させる方法について詳しく知りたいです。何よりもしっかりとした練習として、WPF と C# を使用してこの単純なスプライト エンジンを作成しています。これは C++ で簡単に解決できますが、状況が悪化するたびに手を空中に投げて Win32 に戻るのではなく、管理された環境でこれらの問題を解決する方法を理解する必要があります。

4

6 に答える 6

29

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

インターフェイス ソリューションは、私には意味がありません。(正直に言うと、インターフェースは私にはあまり意味がありませんでした...)

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

于 2009-08-17T18:52:32.377 に答える
18

1 つのアプローチは、System.Linq.Queryable のように、拡張メソッドをインターフェイスと共に使用して、"派生クラス" の実装を提供することです。

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"; } }
}

使用法:拡張メソッドが定義されている名前空間に対する using ディレクティブがある (または同じ名前空間にある) 限り:

var cc = new CustomizableControl();
var header = cc.GetXamlHeader();
于 2009-08-17T19:00:32.187 に答える
7

私はこれを見ていてCustomizableObject、インターフェースにすることを叫んでいます(そして、すべての具象型はオブジェクトに変換できるため、名前のその部分は冗長です)。あなたが直面している問題は、共有されるか、実装によってわずかに異なるいくつかの基本的なロジックを保持する方法がわからないことであり、このロジックをツリー自体に格納して、ポリモーフィックに動作するようにすることです (それは一言?)。

これは、デリゲートを通じて実現できます。どのメンバーが問題を引き起こしているのか正確にはわかりませんが、おそらく次のようなものです。

 ____________________________________      _____________________________________
| 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>)。

于 2009-08-17T19:03:52.727 に答える
2

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

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 は、配信されたオブジェクトと同じ動作をします (両方の動作を継承します)。

于 2013-04-03T21:39:28.610 に答える
2

そんな時はミックスインを使います。ミックスインは、実行時にクラスに機能を追加するために多くの言語で使用される強力な概念です。ミックスインは多くの言語でよく知られています。re-mix フレームワークは、mixin テクノロジを .NET にもたらすフレームワークです。

アイデアは単純です。クラスを mixin を表す class 属性で装飾します。実行時に、この機能がクラスに追加されます。したがって、多重継承をエミュレートできます。

問題の解決策は、CustomizableObject を mixin にすることです。これにはリミックス フレームワークが必要です。

[Uses(CustomizableObjectMixin)] CustomizableControl : UserControl

詳細については、remix.codeplex.com をご覧ください。

于 2011-03-24T08:42:11.530 に答える
2

インターフェイスとオブジェクト構成に頼る必要があるようです。

于 2009-08-17T18:52:18.147 に答える