【问题标题】:Chess in Java - Object orientation vs EfficiencyJava中的国际象棋 - 面向对象与效率
【发布时间】:2015-08-27 11:05:40
【问题描述】:

我正在用 Java 开发一个国际象棋程序并考虑以下问题:

  • Rook 部件需要实现直线移动。
  • Bishop 部件需要实现以沿对角线移动。

然而,

  • Queen 棋子需要实现上述两种运动模式。

我想不出一个干净的解决方案来建模这种关系,我考虑过一些但没有一个既符合良好的面向对象设计代码效率。

  1. Java 不支持多重继承,所以 Queen 不能借用 Rook 和 Bishop 的实现

  2. 如果 Rook 和 Bishop 扩展 Queen,我需要将每种运动的逻辑提取到单独的方法中,这会严重影响我当前的运动验证设计。

上述解决方案似乎都不够优雅,无法击败:

  1. 将所有的运动实现放入所有部分的父类中,这样它们就可以共享所有的通用实现(其中相当多)

我知道解决方案 3 违反了良好的 Java 设计,但在这种情况下,这种设计模式似乎只是在强制解决臃肿、不优雅的解决方案。

也许这可以通过程序的完全重组来避免,但到目前为止一切对我来说看起来都非常有效,好的 OO 设计总是以功能和结构的直接性为代价吗?我的方法对语言的风格是错误的吗?

你会如何解决这个问题?

【问题讨论】:

  • 在处理最终的、真正的实现时,我总是选择效率而不是好的标准等等。也就是说,正如已经建议的那样,您可以在其自己的类实例中将每个动作外部化,然后为每个部分提供它需要的动作。
  • 不仅仅是子类化。使用接口和委托!
  • 请记住,当前的设计指南建议优先考虑组合而不是继承。为什么没有直线和对角线移动生成器类并让棋子类使用它们?话虽如此,如果您在这里编写一个实际的播放引擎,那么请忘记物体的碎片并使用位板 - 您将需要它来提高速度。

标签: java oop chess


【解决方案1】:

虽然某些类的行为方式可能相似,但这并不意味着它们就在一个层次结构中!

例如,HumanCrab 都可以横向移动,但 Human 扩展 Crab 是愚蠢的。

如果你真的想重用运动代码,你可以使用封装,使用Movement类型并使其如下:

class Human
{
   List<Movement> movements;
}

class Crab
{
    List<Movement> movements;
}

class MoveSideWays extends Movement
{
      move();
}

class MoveForward extends Movement
{
      move();
}

但这感觉像是过度设计。我会有一个带有getPossibleMoves()Piece 类,然后直接实现它。没有太多的重叠,而且 Rooks 也有专门的动作(Castling)。

class Rook extends Piece
{
   List<Move> getPossibleMoves()  {...}
}

class Queen extends Piece
{
   List<Move> getPossibleMoves() {...}
}

【讨论】:

  • 我同意,我宁愿创建Movements 的层次结构,而不是棋子
  • 关键是只有一个地方保存你的移动代码。这可以防止代码重复并使您的实现更不容易出错,因为代码更改会影响许多其他实体,因此如果您想更改运动算法,则无需在其他地方查找相同的代码。我同意这个解决方案:-)
  • @Westranger 这个案例是一个有趣的案例,因为您不想重复自己,同时又想要简单、可维护的代码。抽象出棋子的移动方式使得更难看出它们可以直接做什么,也更难对异常进行编码(rook -> castling)。因为永远不会有其他棋子,所以抽象的价值并没有那么大(Queen 只能重用 Bishop 和 Rook 的部分)。我不会打扰并尽可能简单。两种解决方案都可以,恕我直言,都可以。只是口味问题:)
  • 接受,这对我来说似乎是最好的方法,感谢您的意见。
【解决方案2】:

您不应该将超类-子类关系添加到各个部分。女王不是车的特例,车也不是女王的特例。它们只是不同的部分。您可以创建一个接口(或抽象类)Piece 并提供不同的实现,如RookBishopQueen 等。

【讨论】:

    【解决方案3】:

    您可以将这两个方法移至超类,但将它们设为protected static(您也可以将这些方法放在PieceHelper 中)。然后,您可以在子类中实现适当的方法,引用其中一种或另一种方法(使它们成为public)。

    超类:

    abstract class Piece {
        protected static void moveStraight(Piece p) {...}
        protected static void moveDiagonal(Piece p) {...}
    }
    

    (可选)接口:

    interface DiagonalMover {
         void moveDiagonal();
    }
    interface StraightMover {
         void moveStraight();
    }
    

    子类:

    class Rook extends Piece implements StraightMover {
        public void moveStraight() {Piece.moveStraight(this);}
    }
    class Bishop extends Piece implements DiagonalMover {
        public void moveDiagonal() {Piece.moveDiagonal(this);}
    }
    class Queen extends Piece implements StraightMover, DiagonalMover {
        public void moveStraight() {Piece.moveStraight(this);}
        public void moveDiagonal() {Piece.moveDiagonal(this);}
    }
    

    【讨论】:

    【解决方案4】:

    直线和对角线运动是行为 因此,它们可以使用比继承更合适的接口来建模

    Rookie 实现 StraightMovement

    Bishiop 实现 DiagonalMovement

    Queen 实现 StraightMovement、DiagonalMovement

    【讨论】:

      【解决方案5】:

      Java 不支持多重继承,所以 Queen 不能借用 Rook 和 Bishop 的实现

      您不需要多重继承。其中很多都可以使用默认方法进行模拟。

      【讨论】:

      • 链接不会指向任何有用的文章,只是被重定向到公司的主页。
      猜你喜欢
      • 2014-03-02
      • 1970-01-01
      • 2022-06-17
      • 1970-01-01
      • 1970-01-01
      • 2015-07-16
      • 2013-05-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多