I'm working on a project(a game) which will have a board to play on.
I want to make this board as generic as possible in order to be able to re-use the code for a possible other game.
public class Board {
private int rows;
private int cols;
private Box[][] board;
public Board(int rows, int cols){
Box[][] board = new Box[rows][cols];
for (int row = 0; row < this.rows; row++){
for (int col = 0; col < this.cols; col++){
board[row][col] = new Box();
}
}
}
public int getCols(){return this.cols;}
public int getRows(){return this.rows;}
public Piece getContentAtPos(int row, int col){
return board[row][col].getContent();
}
}
This is the Board
class. The problem here is that I have to return a Piece
object with the getContentAtPos()
method because the Box
(Cell) class is like this:
public class Box {
private boolean empty;
private Piece content;
public Box(){
empty = true;
}
public Box(Piece piece){
empty = false;
this.content = piece;
}
public boolean isEmpty(){
return empty;
}
public Piece getContent(){
if (!empty) {
return content;
} else {
return null;
}
}
}
Whereas, the ideal for me would be the class Box
to be able to host any type of object and return this generic object with getContentAtPos()
via the Box getContent()
method.
My generic Box
class.
public class Box<T>{
private boolean empty;
private T content;
public Box(){
empty = true;
}
public Box(T content){
this.content = content;
empty = false;
}
public boolean isEmpty(){
return empty;
}
public T getContent(){
if (!isEmpty()){
return content;
}
return null;
}
public T setContent(Object content){
this.content = content;
}
}
But what do I need to change in the Board class?
What return value shall I put there?
the ideal for me would be the class
Box
to be able to host any type of object and return this generic object withgetContentAtPos
via theBox
getContent
method
I guess you expect your game pieces to declare a certain behavior. If so, Box
doesn't need to store an arbitrary object, but an object of type representing a contract that encompass all the methods which a game piece is expected to have.
In other words, you can define an interface, let's say Piece
, which will have several implementations:
public interface Piece {
// behavior of game pieces
}
public class MyGamePiece implements Piece {
// implementations
}
You don't need to make Box
class to be generic, instead it can contain a field of type Piece
:
public class Box {
private boolean empty;
private Piece content;
// constractor and methods
public void setContent(Object content){
this.content = content;
}
public Piece getContent(){
return content;
}
}
That gives you a freedom to provide any implementation of the Peice
via constructor of the Box
class or true the setter. It means you can design the core part of the gave having only one implementation MyGamePiece
, and later on add a few more classes like NewGamePeace
(names should reflect the purpose of the class if the clear and concise way, it's just dummy example).
The key point is that you can benefit from the polymorphism and don't have to change the previously designed and tested code in order to make it work with new classes (that called a late binding):
Box box = new Box();
box.setContent(new MyGamePiece());
box.setContent(new NewGamePeace()); // in order to make this line compile only need to introde the `NewGamePeace` class itself
And method getContentAtPos()
will also return an object of type Piece
.
I want to make this board as generic as possible in order to be able to re-use the code for a possible other game.
That's a laudable aim.
Your classes have to be loosely coupled and narrow focused in order to be reusable. I.e. each class should a narrow and well-defined set of functionality and be aware only of those object that are crucial for it work (reduce coupling).
And you can make the necessary coupling loose (high coupling isn't good, but classes can't interact without coupling at all, you need to maintain balance) by introducing interfaces as shown above.
Even if eventually you would not use this code in other project, keeping it loosely coupled is beneficial because it makes it easier to maintain and expand the code.