あなたのようにリフレクションを使用することは理想的ではありません。Piece クラスの名前を変更し、クライアントがハードコーディングされた完全修飾クラス名を渡すとすぐに、アプリが壊れます。Elite Gent の提案はその問題を回避しますが、それでもクライアントは具体的なクラスを知る必要があります。これはまさにファクトリ パターンが隠そうとするものです。私の意見では、列挙型を使用してピース型を表し、それをクライアントに引数としてファクトリ メソッドに渡すか、型ごとに個別のファクトリ メソッドを作成することをお勧めします (実行可能な 6 つのピース型を使用)。
とにかく先延ばしにしているので、列挙型アプローチの例を次に示します。
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);
}
}
}
自己完結型の例を維持するために、ここではすべてを静的にしました。通常、Field、Piece、AbstractPiece、および Rook はトップレベルのモデル クラスであり、PieceFactory もトップレベルです。
これは、私が思うあなたの例に行くためのより良い方法であり、例外処理の問題を回避します。
それに戻ると、検討できるアプローチがいくつかあります(リフレクションアプローチに基づいて):
あなたが行ったように Throwable をスローすることは、すべてのエラーをひとまとめにし、クライアントにとってエラー処理が非常に面倒で不透明になるため、悪い習慣です。他に選択肢がない場合を除き、これを行わないでください。
チェックされたすべての例外に対して、メソッドで「スロー」を宣言します。これが有効かどうかを判断するには、スローしている例外の種類をクライアントが認識して理解する必要があるかどうかを検討してください。あなたの例では、Rook を作成したいクライアントは、リフレクション コードによってスローされた InstantiationException、IllegalAccessException、および ClassNotFoundException を知って理解する必要がありますか? おそらくこの場合ではありません。
クライアントがキャッチする必要のない実行時例外でそれらをラップします。これは必ずしも良い考えではありません。呼び出しているコードがチェック例外をスローしているという事実には、通常、理由があります。あなたの例では、リフレクションを行っていましたが、これは多くの点でうまくいかない可能性があります (API は LinkageError、ExceptionInInitializerError、ClassNotFoundException、IllegalAccessException、InstantiationException、および SecurityException を宣言します)。チェックされた例外を実行時例外にラップしても、その問題は解決しません。私はこれを「コードの匂い」だと考えています。エラーが回復不能なシステム障害を意味する場合、それは有効な選択かもしれませんが、ほとんどの場合、そのような障害をより適切に処理する必要があります。
完全なサブシステムに対してカスタム チェック例外をスローします。たとえば、すべての実装固有のデータ アクセス例外をラップするために使用される Spring の org.springframework.dao.DataAccessException を参照してください。これは、クライアントが 1 つの例外タイプだけをキャッチする必要があり、必要に応じて詳細を把握できることを意味します。あなたの場合、チェック済みの PieceCreationException を作成し、それを使用してすべての反射エラーをラップできます。これは有効な例外処理パターンですが、PieceFactory にとっては少し扱いにくいと思います。
null を返します。コード内のすべての例外をキャッチし、発生した場合は単純に null を返すことができます。JavaDoc にこれが明確に示されていることを確認してください。このアプローチの欠点は、クライアントがどこでも null をチェックしなければならない可能性があることです。
特定のエラー タイプを返します。これは、Java コア API で少し前に見た、こっけいな (非常にオブジェクト指向の) アプローチです (くそー、どこだったか思い出せません)。このために、追加の Piece タイプを作成します。
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");
}
}
作成中にエラーが発生した場合は、この型のインスタンスを返します。利点は、単純な null 値を返すよりも自己文書化できることですが、クライアントはもちろん のようなものを使用してこれを確認する必要がありますinstanceof
。そうでない場合、Piece メソッドが呼び出されたときに UnsupportedOperationException がスローされます。少し重いですが、非常に明確です。ここまで行くかどうかはわかりませんが、それでも興味深いアイデアです。