是否可以使用空方法并在子类中重写它? 这就是我的代码中的样子。
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中)很昂贵。我将确保在搜索过程中没有创建任何对象。
答案 0 :(得分:5)
虽然拥有一个什么都不做的空方法肯定是可以的,但你应该评估它的替代方法 - 一个完全缺失的空方法,即 abstract 方法。
这可能适用于您的情况,因为您将创建ShortRook
,LongRook
或提升的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定义特殊的方法,因为他们都知道他们可以做出的动作。
这会大大压平您的类层次结构,并使处理和评估板更容易推理。