Using reflection like you do is not ideal. As soon as you rename a Piece class and clients pass hardcoded fully-qualified-classnames, your app will break. The Elite Gent's suggestion avoids that problem, but still requires clients to know the concrete class, which is exactly what the factory pattern tries to hide. A better approach in my opinion would be to either use an enum to represent Piece types and let the client pass that as the argument to your factory method, or create separate factory methods for each type (with 6 piece types that is feasible).
Since I am procrastinating anyway, here an example of the enum-approach:
import java.util.ArrayList;
import java.util.List;
class PieceFactoryExample {
static enum PieceFactory {
ROOK {
Piece create() {
return new Rook();
}
};
abstract Piece create();
}
static class Field {
char line;
int row;
public Field(char line, int row) {
this.line = line;
this.row = row;
}
public String toString() {
return "" + line + row;
}
}
static interface Piece {
void moveTo(Field field);
List<Field> possibleMoves();
}
static abstract class AbstractPiece implements Piece {
Field field;
public void moveTo(Field field) {
this.field = field;
}
}
static class Rook extends AbstractPiece {
public List<Field> possibleMoves() {
List<Field> moves = new ArrayList<Field>();
for (char line = 'a'; line <= 'h'; line++) {
if (line != field.line) {
moves.add(new Field(line, field.row));
}
}
for (char row = 1; row <= 8; row++) {
if (row != field.row) {
moves.add(new Field(field.line, row));
}
}
return moves;
}
}
public static void main(String[] args) {
Piece pawn = PieceFactory.ROOK.create();
Field field = new Field('c', 3);
pawn.moveTo(field);
List<Field> moves = pawn.possibleMoves();
System.out.println("Possible moves from " + field);
for (Field move : moves) {
System.out.println(move);
}
}
}
I made everything static here to keep the example self-contained. Normally Field, Piece, AbstractPiece and Rook would be top-level model classes and PieceFactory would be top-level too.
This is a better way to go for your example I think, and avoids the exception handling issue.
Returning to that, there are a couple of approaches you can consider (based on your reflection approach):
Throwing Throwable like you did is bad practice since it lumps together all errors and makes error handling very cumbersome and untransparent for clients. Do not do that unless you have no other options.
Declare 'throws' on your method for all checked exceptions. To decide if this is valid, consider if the client should know and understand the exception type you are throwing. In your example, should the client that wants to create a Rook know and understand the InstantiationException, IllegalAccessException and ClassNotFoundException thrown by your reflection code? Probably not in this case.
Wrap them in a runtime exception which needs not be caught by clients. This is not always a good idea. The fact that code you are calling is throwing checked exceptions usually has a reason. In your example you were doing reflection, and this can go wrong in many ways (the API declares LinkageError, ExceptionInInitializerError, ClassNotFoundException, IllegalAccessException, InstantiationException and SecurityException). Wrapping the checked exceptions in a runtime exception does not make that problem go away. I consider doing this a 'code smell'. If the error means an unrecoverable system failure, then it might be a valid choice, but in most cases you would want to handle such failures more gracefully.
Throw a custom checked exception for a complete subsystem. See for example Spring's org.springframework.dao.DataAccessException which is used to wrap all implementation specific data access exceptions. This means clients will have to catch just one exception type and can figure out the details if they need to. In your case you could create a checked PieceCreationException and use that to wrap all the reflection errors. This is a valid exception handling pattern, but I think it might be a little to heavy-handed for your PieceFactory.
Return null. You could catch all the exceptions within your code and simply return null if they occur. Just make sure your JavaDoc clearly indicates this. A drawback of this approach is that clients might have to check for nulls everywhere.
Return a specific error type. This is a geeky (very object-oriented) approach I saw in the Java core API somewhere a while back (darn, can't remember where). For this you would create an extra Piece type:
class NullPiece implements Piece {
public void moveTo(Field field) {
throw new UnsupportedOperationException("The Piece could not be created");
}
public List<Field> possibleMoves() {
throw new UnsupportedOperationException("The Piece could not be created");
}
}
And return an instance of this type when an error occurs during creation. The advantage is that it is more self-documenting than returning a simple null-value but clients would of course have to check for this using something like instanceof
. If they don't, then they will encounter the UnsupportedOperationException thrown when the Piece methods are called. A bit heavy, but very explicit. I'm not sure I would go this far, but it's still an interesting idea.