Mit Reflektion wie Sie tun ist nicht ideal. Sobald Sie eine Piece-Klasse umbenennen und Clients fest codierte vollständig qualifizierte Klassennamen übergeben, wird Ihre App unterbrochen. Der Vorschlag von Elite Gent vermeidet dieses Problem, erfordert jedoch immer noch, dass die Kunden die konkrete Klasse kennen, die genau das ist, was das Fabrikmuster zu verbergen versucht. Ein besserer Ansatz wäre meines Erachtens, entweder eine Enumeration zu verwenden, um Stücktypen darzustellen, und den Client als Argument für Ihre Factory-Methode zu übergeben, oder separate Factory-Methoden für jeden Typ zu erstellen (mit 6 Stücktypen, die machbar sind).
Da ich sowieso bin zaudern, hier ein Beispiel für den ENUM-Ansatz:
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);
}
}
}
ich hier alles statisch machte das Beispiel umluftunabhängigem zu halten. Normalerweise wären Field, Piece, AbstractPiece und Rook Top-Level-Modellklassen und PieceFactory wäre auch Top-Level.
Dies ist ein besserer Weg für Ihr Beispiel, denke ich, und vermeidet das Problem der Ausnahmebehandlung.
Zurückkommend auf das, es gibt ein paar Ansätze können Sie (auf der Grundlage Ihrer Reflexion Ansatz) betrachten:
Werfen Throwable wie Sie ist eine schlechte Praxis haben, da es alle Fehler Klumpen zusammen und macht die Fehlerbehandlung sehr umständlich und undurchsichtig für Kunden. Tun Sie das nicht, wenn Sie keine anderen Optionen haben.
Deklarieren Sie Ihre Methode für alle geprüften Ausnahmen. Um zu entscheiden, ob dies gültig ist, überlegen Sie, ob der Client den Exceptionstyp, den Sie werfen, kennen und verstehen sollte. In Ihrem Beispiel sollte der Client, der einen Rook erstellen möchte, die InstantiationException, IllegalAccessException und ClassNotFoundException, die von Ihrem Reflektionscode ausgelöst werden, kennen und verstehen? Wahrscheinlich nicht in diesem Fall.
Wickeln Sie sie in eine Laufzeitausnahme ein, die von Clients nicht abgefangen werden muss. Das ist nicht immer eine gute Idee. Die Tatsache, dass der von Ihnen aufgerufene Code Ausnahmen auslöst, hat normalerweise einen Grund. In Ihrem Beispiel haben Sie Reflektionen durchgeführt und dies kann auf viele Arten schiefgehen (die API deklariert LinkageError, ExceptionInInitializerError, ClassNotFoundException, IllegalAccessException, InstantiationException und SecurityException). Das Einhüllen der geprüften Ausnahmen in einer Laufzeitausnahme macht dieses Problem nicht überflüssig. Ich halte das für einen "Code-Geruch". Wenn der Fehler einen nicht behebbaren Systemfehler bedeutet, ist dies möglicherweise eine gute Wahl, aber in den meisten Fällen möchten Sie solche Fehler besser behandeln.
Eine benutzerdefinierte geprüfte Ausnahme für ein vollständiges Subsystem werfen. Siehe z. B. Spring's org.springframework.dao.DataAccessException, mit der alle implementierungsspezifischen Datenzugriffsausnahmen umbrochen werden. Dies bedeutet, dass Clients nur einen Ausnahmetyp erfassen müssen und die Details bei Bedarf ermitteln können. In Ihrem Fall könnten Sie eine überprüfte PieceCreationException erstellen und diese verwenden, um alle Reflexionsfehler zu umbrechen. Dies ist ein gültiges Ausnahmebehandlungsmuster, aber ich denke, es könnte für Ihre PieceFactory ein wenig zu schwerfällig sein.
Rückgabewert null. Sie können alle Ausnahmen in Ihrem Code abfangen und einfach null zurückgeben, wenn sie auftreten. Stellen Sie nur sicher, dass Ihr JavaDoc dies eindeutig anzeigt. Ein Nachteil dieses Ansatzes ist, dass Clients überall nach Nullen suchen müssen.
Geben Sie einen spezifischen Fehlertyp zurück. Dies ist ein geeky (sehr objektorientierter) Ansatz, den ich irgendwo in der Java Core API gesehen habe (verdammt, kann mich nicht erinnern wo).
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");
}
}
und geben eine Instanz dieses Typs, wenn ein Fehler bei der Erstellung auftritt: Dazu würden Sie ein zusätzliches Stück Art erstellen. Der Vorteil ist, dass es selbstdokumentierender ist, als einen einfachen Null-Wert zurückzugeben, aber Clients müssten dies natürlich mit etwas wie instanceof
überprüfen. Wenn dies nicht der Fall ist, wird die UnsupportedOperationException ausgelöst, wenn die Piece-Methoden aufgerufen werden. Ein bisschen schwer, aber sehr explizit. Ich bin mir nicht sicher, ob ich so weit gehen würde, aber es ist immer noch eine interessante Idee.
Warum behandeln Sie Ihre Ausnahmen nicht in den Zeilen, die sie in 'createPiece' erzeugen? – justkt
Viel besser, Reflexion zu vermeiden. –
Danke allen! Ich habe meinen Code ein wenig geändert und dieses Mal keine Reflektion benutzt. –