繁体   English   中英

这是滥用类型系统吗?

[英]Is this an abuse of the type system?

我正在开发巴洛克国际象棋游戏的爱好项目。 对于那些没有玩过它的人,它具有与国际象棋相同的基本规则,但移动和捕捉的方法是不同的。

当然,我为游戏创建了标准类: GameStateBoardSquare ,以及为从BasePiece继承的每件作品指定的类。

每件作品都有2个主要的虚拟方法, GetPossibleMoves(Board board)GetCapturedSquares(Board board, Square toSquare)

现在,其中一件作品, 模仿者 ,通过“模仿”它捕获的作品来捕捉碎片。 例如,Long Leaper可以通过跳过它们捕获碎片。 这意味着模仿者可以跳过敌人的Long Leapers捕捉它们(但不能跳过任何其他东西)。

我为除了Imitator之外的所有部分完成了GetCapturedSquares()功能(这绝对是最狡猾的部分)。

我对模仿者的基本算法是:

  • 找到所有带有敌人棋子的方块
  • 对于每个敌人......
    • 在与模仿者相同的位置创建一个模拟片段
    • 找到有效的捕获,如果模拟的部分移动到选定的方块
    • 验证敌方是否在捕获的方块列表中

由于我已经为其他部分的动作编写了代码,我想我会实例化新的部分并使用他们的GetCapturedSquares()方法,具体取决于敌人的类型。 为此,我设置了一个Dictionary ,你可以在这里看到将System.Type映射到所述类型的实例化对象:

var typeToPiece = new Dictionary<Type, BasePiece>() 
{
    {typeof(Pincer), new Pincer() { Color = this.Color, CurrentSquare = this.CurrentSquare}},
    {typeof(Withdrawer), new Withdrawer() { Color = this.Color, CurrentSquare = this.CurrentSquare }},
    {typeof(Coordinator), new Coordinator() { Color = this.Color, CurrentSquare = this.CurrentSquare }},
    {typeof(LongLeaper), new LongLeaper() { Color = this.Color, CurrentSquare = this.CurrentSquare }},
    {typeof(King), new King() { Color = this.Color, CurrentSquare = this.CurrentSquare }},
};
//...
var possibleMoves = typeToPiece[enemySquare.Occupant.GetType()].GetPossibleMoves(board, toSquare);

这样做会让我内心感到肮脏。 创建一个表示作为字典键的片段类型的enumstring更合适,还是真的无关紧要? 有没有不同的方法来处理这个? 我认为它的方式很好,但我有兴趣听听你的想法。

我认为你应该向BasePiece添加一个abstract方法,它“克隆”当前的片段并返回模拟片段。

override在每个片段类型中override此方法。 要在它们之间共享代码,可以向基类添加protected方法,将共享属性复制到传递给它的实例:

abstract class BasePiece {
   protected BasePiece(BasePiece pieceToClone) {
      this.Color = pieceToClone.Color;
      this.CurrentSquare = pieceToClone.CurrentSquare;
   }
   public abstract BasePiece GetSimulatedClone();
}

class King : BasePiece {
   protected King(King pieceToClone) : base(pieceToClone) { }
   public King() { }
   public override BasePiece GetSimulatedClone() {
       return new King(this);
   }
}

一般来说,每当你根据变量的类型进行切换时,你应该三思而后行,看看你是否可以改变多态性。

这让我觉得更优雅:

    private abstract class Piece {}
    private class King : Piece { }
    private class Imitator : Piece { }

    private void main(object sender, EventArgs e) {
        Piece x;
        x = CreateNewPiece(new King());
        x = CreateNewPiece(new Imitator());
    }

    private T CreateNewPiece<T>(T piece) where T : Piece, new() {
        return new T();
    }

它依赖于new()泛型约束来实例化一个类型变量。

我个人同意你可以做你正在做的事情,因为它是一个小的业余爱好项目。 但是,如果你真的想解决你所描述的问题,你可以创建一个单独的Mover对象层来处理围绕不同部分实际移动的逻辑。 然后,不是要求一块它可以做什么动作,而是将该块的位置信息以及板状态信息传递给Mover,Mover告诉你该块可以做出什么动作。 然后,与模仿者相关联的Mover可以是您描述的所有其他移动者的组合,但不需要动态创建假“碎片”。

我做的另一个建议,就是逻辑而不是你的模型,更改你的逻辑是这样的:

  • 对于每种类型(或移动器类型)
    • 在与模仿者相同的位置创建一个模拟片段
    • 找到有效的捕获,如果模拟的部分移动到选定的方块
    • 只保留敌人广场的占用者所在的捕捉物品

这是一个微妙的差异,但会显着减少所需的计算量。

更新

Recursive的评论让我意识到我可能不太清楚Mover层如何工作。 想法是拥有一个KingMover,PincerMover等等,它们知道特定棋子类型的动作。 由于它们与一件作品类型而非作品本身相关,因此它们甚至可能是单身人士。 每个部分类型都可以有一个指向其移动者的Mover字段,然后您的业务逻辑可以直接调用Mover的GetPossibleMoves方法,或者您的部件的GetPossibleMoves方法可以简单地调用Mover的方法,将其自身作为参数传递。 ImitatorMover会知道要求彼此类型的移动器进行可能的移动,然后根据它们是否会攻击与该移动器相关联的类型来过滤这些移动。

你有几乎与当前系统相同的代码,但每件作品的代码实际上只能专注于表示该作品的信息(位置,颜色等),而实际确定作品如何移动的代码则是进入一个单独的类层。 每个班级都有一个目的。

在没有触及这个特定问题的情况下,字典的概念没有任何内在错误,它允许您按类型查找对象。 事实上,.NET FCL已经提供了这样一种类型 - 它叫做KeyedByTypeCollection<T> 对于这样的事情可能更好,因为它确保对象的键是GetType() (而不是其他随机类型)返回的类型,并且不允许添加两个相同类型的对象。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM