This is, unfortunately, a highly opinionated question, and there is no perfect answer here. However, I believe many would agree that your insights in creating Design 2 after Design 1 are, on the whole, strong and thus make Design 2 objectively better in a number of ways.
You hit the nail on the head nice and heard with removing the Board
reference from the Piece
definition, and the Square
as well. A good OOO design principle is that Objects should only store references to that which conceptually defines them. The Board
, and Square
(BoardPosition
?) do not define the Piece.
The moves(...)
method is a tricky one. While the Piece
class seems nice, I would argue that the Board
class is better, but still not enough. If you intend to create a fully accurate Chess implementation, you will have to design for En passant, Castling, and Stalemate conditions, all of which depend not only on the current Board
state, but previous Board
states as well - the transactional history of the ChessGame
, if you will.
I could go on and on here but you deserve to learn more of this for yourself. So, I will leave a few more questionably objective nuggets for your consumption:
- Be sure to use Enums vigilantly!
PieceType
andColor
are perfect candidates for this. Enums make your code precise and clear, and tend to turn nasty Runtime errors into easy Compilation errors for equivalent designs. - Always design with your end goals in mind. If you plan on doing some sort of AI experimentation with these classes, revisit your decision to make
Board
immutable. This may be more prohibitive than you want for fast analysis. - In direct contradiction with my previous point, don't worry about interfaces until you know you need at least two different implementations, and you know what they are. Speculative abstraction is more harmful than short-term limitations.