Обычно считается плохой практикой, по крайней мере в Java и C ++, использовать наследование для повторного использования кода (см. «Эффективная Java» Джошуа Блоха в качестве аргументов). Вместо этого лучше использовать состав (или какую-либо другую форму делегирования).
В вашем случае я бы полностью изменил ситуацию, так что Piece
- это final class
, просто взяв enum
, представляющий тип куска:
public final class Piece {
public static enum Type {
PAWN {
List<Move> getLegalMoves(Color c, Location l) { /* ... */ }
},
BISHOP { /* ... */ },
KNIGHT { /* ... */ },
ROOK { /* ... */ },
QUEEN { /* ... */ },
KING { /* ... */ }; // i hope i didn't forget anything :P
abstract List<Move> getLegalMoves(Color c, Location l);
// ... etc.
}
private final Type type;
private final Color color;
private Location location;
public Piece(Type type, Color color, Location location) {
this.type = type;
this.color = color;
this.location = location;
}
public List<Move> getLegalMoves() {
return type.getLegalMoves(color, location);
}
// ... etc.
}
Таким образом, вся логика, общая для всех частей, реализуется один раз в Piece
, а различные части заключены в Type
. И если вам когда-либо понадобится узнать, на какую часть вы смотрите, вам не нужно использовать instanceof
и приведение или что-то еще, вы просто реализуете Piece.getType()
и switch
для этого.