有什么方法可以避免子类中的Object / casts吗?

时间:2016-04-18 05:22:00

标签: c# .net oop design-patterns

遵循这本书"人工智能 - 现代方法"我试图使搜索算法实现(DFS,A *等)可以解决不同的问题(从A到B的路径,滑块拼图等),而不仅仅是一个特定的问题。

public abstract class Problem
{
    protected Problem(object initialState)
    {
        InitialState = initialState;
    }

    public object InitialState { get; }

    /// <summary>
    /// Checks if the state is the goal state
    /// </summary>
    public abstract bool IsGoalState(object state);

    /// <summary>
    /// Returns the actions available from the state
    /// </summary>
    public abstract ISet<Action> Actions(object state);

    /// <summary>
    /// Returns the state that results after performing the action on the state
    /// </summary>
    public abstract object ResultState(object state, Action action);

    /// <summary>
    ///  Returns the cost of action to reach from state to reachedState
    /// </summary>
    /// <param name="state">The state from which the action will be performed</param>
    /// <param name="action">One of the actions available in the state</param>
    /// <param name="reachedState">The state that results after performing the action</param>
    public virtual int StepCost(object state, Action action, object reachedState)
    {
        return 1;
    }
}

_

public class Node
{
    public Node(object state)
    {
        State = state;
        PathCost = 0;
    }

    /// <summary>
    /// </summary>
    /// <param name="action">The action that was applied to the parent to generate the node</param>
    /// <param name="stepCost">The cost from the parent node to this node</param>
    public Node(object state, Node parent, Action action, int stepCost)
        : this(state)
    {
        Parent = parent;
        Action = action;
        if (Parent != null)
            PathCost = Parent.PathCost + stepCost;
    }

    public object State { get; }

    public Node Parent { get; }

    /// <summary>
    /// The action that was applied to the parent to generate the node
    /// </summary>
    public Action Action { get; }

    /// <summary>
    /// The cost of the path from the initial statee to the node
    /// </summary>
    public int PathCost { get; }

    public bool IsRootNode => Parent == null;

    public IEnumerable<Node> PathFromRoot()
    {
        var path = new Stack<Node>();

        var node = this;
        while (!node.IsRootNode)
        {
            path.Push(node);
            node = node.Parent;
        }
        path.Push(node); // root

        return path;
    }
}

_

public abstract class Action
{
    /// <summary>
    /// true if it is a "No Operation" action
    /// </summary>
    public virtual bool IsNoOp()
    {
        return false;
    }

    public string Name => GetType().Name;

    public override string ToString()
    {
        return Name;
    }
}

public class NoOp : Action
{
    public override bool IsNoOp()
    {
        return true;
    }
}

public abstract class EightPuzzleAction : Action
{
}

/// <summary>
/// Move the blank tile to the left
/// </summary>
public class MoveLeft : EightPuzzleAction
{
}
// the same MoveRight, MoveTop, MoveBottom

为了做到这一点,我可以从这个基类Problem类实现方法,并将该具体实例传递给搜索算法(接受Problem)。

class EightPuzzleProblem : Problem
{
    private readonly int[,] _goalState =
    {
        {0, 1, 2},
        {3, 4, 5},
        {6, 7, 8},
    };

    public EightPuzzleProblem(int[,] initialState) : base(initialState)
    { }

    public override bool IsGoalState(object state)
    {
        var puzzleState = (int[,]) state;

        ......... <check if puzzleState is the same as _goalState>
    }
    ............
}

class DepthFirstSearch
{
    public IEnumerable<Action> Search(Problem p)
    {
        return DfsRecursive(p, new Node(p.InitialState));
    }

    public IEnumerable<Action> DfsRecursive(Problem p, Node node)
    {
        if (p.IsGoalState(node.State))
            return node.PathFromRoot();

        .......<simple DFS implementation>
    }
}

// usage example

var dfs = new DepthFirstSearch();
var result = dfs.Search(new EightPuzzleProblem(initialState));

if (!result.Any)
   // fail
if (result.Count == 1 && result[0].IsNoOp)
   // already at goal


foreach (var act in result)
{
    if (act is MoveLeft) .....
}

但是我看到我的班级设计有一些不便:在具体的类实现代码中,我需要有很多来自object的强制转换。有什么办法可以避免吗? 如果我将Problem类设为通用并从ConcreteProblem继承Problem<Something>,那么我将无法通过Problem使用它...

2 个答案:

答案 0 :(得分:1)

实际上,您可以将Problem泛型设置为实际状态的泛型参数。

public abstract class Problem<T> where T : IState{ 
    public abstract bool IsGoalState(T state);
}

在派生类中,您现在可以简单地从Problem派生实际类型IState作为通用参数:

class MyDerived : Problem<MyState> {
    public override bool IsGoalState(MyState state) { ... }
}

答案 1 :(得分:1)

为什么不注入你的GoalState,并使用如下界面使GameState也可插入。通过这种方式,您可以在具体类中的两个状态上实现您想要执行的任何操作,而Problem类只需要调用这些函数。你也可以有不同类型的州。

public abstract class Problem<T> where T:IGameState
{
    protected Problem(T initialState)
    {
        InitialState = initialState;
    }

    public T InitialState { get; set; }
    /// <summary>
    /// Checks if the state is the goal state
    /// </summary>
    public abstract bool IsGoalState(T state);


}

public class EightPuzzleProblem<T> : Problem<T> where T : IGameState
{
    private readonly T _goalState;

    public EightPuzzleProblem(T initialState, T goalState)
        : base(initialState)
    {
        _goalState = goalState;
    }
    public override bool IsGoalState(T state)
    {
        return _goalState.IsEqual(state);
    }
}

public interface IGameState
{
    bool IsEqual(IGameState state);
}

public class EightPuzzleGameState : IGameState
{
    private readonly int[,] _goalState =
    {
        {0, 1, 2},
        {3, 4, 5},
        {6, 7, 8},
    };

    public bool IsEqual(IGameState state)
    {
        //Compare state with _goalState
    }
}