1

私は現在、非常に厄介な問題に直面しています。

interface IStateSpace<Position, Value>
where Position : IPosition           // <-- Problem starts here
where Value : IValue                 // <-- and here as I don't
{                                    //     know how to get away this
                                     //     circular dependency!
                                     //     Notice how I should be
                                     //     defining generics parameters
                                     //     here but I can't!
    Value GetStateAt(Position position);
    void SetStateAt(Position position, State state);
}

ここで説明するようにIPositionIValueIStateは相互に依存しています。どうすればこれを回避できますか?この循環依存を回避し、それでも私がやりたいことを正確に説明する他のデザインは考えられません。

interface IState<StateSpace, Value>
where StateSpace : IStateSpace        //problem
where Value : IValue                  //problem
{
    StateSpace StateSpace { get; };
    Value Value { get; set; }
}

interface IPosition
{
}

interface IValue<State>
where State : IState {      //here we have the problem again
    State State { get; }
}

基本的に、内部に状態がある状態空間IStateSpaceがあります。IState状態空間でのそれらの位置は、によって与えられIPositionます。各状態には、1つ(または複数)の値がありますIValue。説明よりも少し複雑なので、階層を単純化しています。この階層をジェネリックスで定義するという考え方は、同じ概念のさまざまな実装を可能にすることです(IStateSpaceグラフなどのマトリックスとして実装されます)。

これで逃げられますか?この種の問題を一般的にどのように解決しますか?これらの場合、どのようなデザインが使用されますか?

ありがとう

4

2 に答える 2

5

あなたが何を達成しようとしているのかわかりません-なぜジェネリックスを使用して具象型をインターフェースに強制したいのですか?これは、インターフェイスの意図に完全に反しているように見えます。具体的な型を使用しないでください。次の非一般的な定義の何が問題になっていますか?

public interface IStateSpace
{
    IState GetStateAt(IPosition position);
    void SetStateAt(IPosition position, IState state);
}

public interface IState
{
    IStateSpace StateSpace { get; }
    IValue Value { get; set; }
}

public interface IPosition
{
}

public interface IValue
{
    IState State { get; }
}

次に、具体的な実装を作成できます。

internal class MatrixStateSpace : IStateSpace
{
    IState GetStateAt(IPosition position)
    {
        return this.Matrix[position];
    }

    void SetStateAt(IPosition position, IState state)
    {
        this.Matrix[position] = state;
    }
}

internal class GraphStateSpace : IStateSpace
{
    IState GetStateAt(IPosition position)
    {
        return this.Graph.Find(position).State;
    }

    void SetStateAt(IPosition position, IState state)
    {
        this.Graph.Find(position).State = state;
    }
}

これで、インスタンスが必要な場所でインスタンスを使用するMatrixStateSpaceかどうかを決定できます。もちろん、他のすべてのタイプにも同じことが当てはまります。GraphStateSpaceIStateSpace

実装の難しい部分は、新しいインスタンスを作成する必要がある場合です。たとえば、でIState AddNewState()定義されたメソッドがあるかもしれませんIStateSpace。の具体的な実装は、具体的なタイプの実装を知らなくても(理想的には)インスタンスIStateSpaceを作成するという問題に直面します。これは、ファクトリ、依存性注入、および関連する概念が関係するものでした。IStateIState

于 2010-05-22T01:22:54.560 に答える
3

問題が何であるかは完全には明らかではありません-はい、ジェネリック型には循環依存関係がありますが、それは機能します。

プロトコルバッファにも同様の「問題」があります。「メッセージ」と「ビルダー」があり、それらはペアになっています。したがって、インターフェースは次のようになります。

public interface IMessage<TMessage, TBuilder>
    where TMessage : IMessage<TMessage, TBuilder> 
    where TBuilder : IBuilder<TMessage, TBuilder>

public interface IBuilder<TMessage, TBuilder> : IBuilder
    where TMessage : IMessage<TMessage, TBuilder> 
    where TBuilder : IBuilder<TMessage, TBuilder>

それは確かに醜いですが、それは機能します。現在表現できないことを何を表現できるようにしたいですか?これについての私の考えのいくつかは私のブログで見ることができます。(ここでは、プロトコルバッファに関するシリーズのパート2とパート3が最も関連性があります。)

(余談ですが、型パラメーターにプレフィックスを追加すると、コードがより従来型になります。現在は、クラスのようにT見え、単なるクラスです。)StateValue

于 2010-05-22T06:21:55.450 に答える