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

就像你在这里一样,两者都IPosition相互依赖。我该怎么办?我想不出任何其他设计可以绕过这种循环依赖,并且仍然准确地描述了我想要做的事情! IValueIState

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

基本上我有一个状态空间IStateSpaceIState里面有状态。它们在状态空间中的位置由 给出IPosition。然后每个状态都有一个(或多个)值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

目前尚不完全清楚问题出在哪里 - 是的,您的泛型类型中有循环依赖关系,但这有效。

我在Protocol Buffers中有一个类似的“问题” :我有“消息”和“构建器”,它们成对出现。所以接口看起来像这样:

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>

这当然很难看,但它确实有效。你想表达什么是你目前无法表达的?你可以在我的博客上看到我对此的一些想法。(关于 Protocol Buffers 系列的第 2 部分和第 3 部分在这里最为相关。)

(顺便说一句,如果您为类型参数添加前缀,它会使您的代码更加传统T。目前它看起来像State并且Value只是类。)

于 2010-05-22T06:21:55.450 回答