是否可以使用空方法?

时间:2015-06-01 16:21:56

标签: java oop subclass hierarchy chess

是否可以使用空方法并在子类中重写它? 这就是我的代码中的样子。

public class Rook() {

    public void voidCastleRight() { }

}

public class ShortRook() extends Rook {

    @Override
    public void voidCastleRight() {
        getPlayer().setkSC(false); //void King Side Castling (Short Castle)
    }

}

public class LongRook() extends Rook {

    @Override
    public void voidCastleRight() {
        getPlayer().setqSC(false); //void Queen Side Castling (Long Castle)
    }
}

上下文是一个国际象棋引擎。它是用Java编写的,它必须搜索下一个“最佳”移动,以便给出一个状态。因此,一切都尽可能高效地实施是很重要的,因为许多方法将被称为数百万次。因此,我想要这种Rooks的等级,而不是一个Rook类,我必须检查Rook在哪一侧并检查Rook是否处于初始位置等等。

首次创建Board时,会有ShortRook和LongRook。随着游戏的进行,有可能因为典当促销而将更多的白嘴鸦引入游戏中。这些将是鲁克的实例。

每当移动Rook时,都会调用方法voidCastleRight()。由于Pawn升级而存在的车辆不应该在移动时使城堡无效(空方法)。自游戏开始以来存在的鲁克斯应该在移动时使城堡权利无效(子类中的方法)。

我还编写了一个解析器,它接受FENStrings并将它们转换为Board,反之亦然。当鲁克斯不在他们的初始位置时,没有办法分辨出Short-LongRook和LongRook。这不是问题,因为无论如何城堡权利已经废除,它们可以被解析为鲁克的实例。因此,如果我将一个Short或LongRook对象输入Rook,只要它已经使相关的城堡无效(即它已移动),那么它会没有问题吗?通过这种方式,当城堡已经无效时,它不会毫无疑问地无效。我不关心这些解析器方法的复杂性,因为它们不会用于搜索。

虽然有些人可能会认为这些想法是微观优化“这是所有邪恶的根源”,但是当这种方法被称为几百万次时,这些优化可能会得到回报。我也更关注OOP范式。

PS:我知道Java不是用于此应用程序的最佳语言,它无关紧要。我知道对象创建(在Java中)很昂贵。我将确保在搜索过程中没有创建任何对象。

3 个答案:

答案 0 :(得分:5)

虽然拥有一个什么都不做的空方法肯定是可以的,但你应该评估它的替代方法 - 一个完全缺失的空方法,即 abstract 方法。

这可能适用于您的情况,因为您将创建ShortRookLongRook或提升的Rook

public abstract class AbstractRook() {
    public abstract void voidCastleRight();
}

public class ShortRook() extends AbstractRook{
    @Override
    public void voidCastleRight() {
        getPlayer().setkSC(false); //void King Side Castling (Short Castle)
    }
}

public class LongRook() extends AbstractRook{
    @Override
    public void voidCastleRight() {
        getPlayer().setqSC(false); //void Queen Side Castling (Long Castle)
    }
}

public class PromotedRook() extends AbstractRook{
    @Override
    public void voidCastleRight() {
        throw new IllegalStateException("Promoted rook cannot castle");
    }
}

答案 1 :(得分:3)

问题标题的答案:

  

是否可以使用空方法?

是一个响亮的 - 这是完全可以接受的情况。

然而 - 我觉得你使用错误的工具来满足你的要求。我不认为禁用castling的功能应该是Rook函数的一部分。你需要的是一个观察者/听众,他们在正确的情况下监视车辆运动并禁用铸造。然后,您可以使用类似的架构来启用/禁用En-Passant,例如。

另请记住Castling Castling只有在国王从未移动时才能完成,所涉及的 rook从未移动 >国王与所涉及的车辆之间的广场空置国王未受到检查,而国王不会越过或结束在广场上在检查 。你可以使用车上的侦听器,国王和移动引擎在一个地方实现这整个规则。

答案 2 :(得分:2)

我会考虑一种非抽象方法,它不会成为code smell

这里的第一个选择是创建一个Rook接口而不是一个类。

如果你想给你的基类具体功能,可能会定义你引用的getPlayer()方法,然后使用抽象类并将方法标记为抽象,你会考虑第二种选择。

与您的问题无关,但我会考虑您的代码的其他两个方面。首先是使用名称" void"在你的方法中,这有点尴尬。

最后,我经常告诉人们重新考虑使用void函数。原因是void函数根据定义不执行任何操作或执行side effect。副作用本身不是很糟糕,但由于它涉及修改状态,它可能会使你的程序更难以推理。

例如,考虑一个方法validMoves(),它返回该片段可以进行的有效移动列表。在这种情况下,您可以使用Piece接口,并在评估可能的移动时将每个部分视为相同:

interface Piece {
    List<Move> validMoves();
}

在这种情况下,你只有一个Rook实例类;一个在国王的两边。你不必为Kings,Rooks和Pawns定义特殊的方法,因为他们都知道他们可以做出的动作。

这会大大压平您的类层次结构,并使处理和评估板更容易推理。