私の工場のクラスに何か問題がありますか?
-
10-10-2019 - |
質問
class PieceFactory {
@SuppressWarnings("rawtypes")
public Piece createPiece(String pieceType) throws Throwable{
Class pieceClass = Class.forName(pieceType);
Piece piece = (Piece) pieceClass.newInstance();
return piece;
}
}
私はすべての例外を処理することに慣れているわけではありませんが、それを投げているだけですが、この工場を使用する方法を使用して、スロー可能な例外を投げる必要があることがわかります。
たとえば、私のクラスの1つには、工場を使用する方法を使用して多くのオブジェクトをインスタンス化する方法があります。例外をスローするだけでそのクラスでメソッドを使用できますが、そのクラスへの参照を別のクラスに渡してからそこからメソッドを使用しようとすると機能しません。その後、例外をキャッチするように強制します。
おそらく工場は必要ありませんが、面白そうに思えたので、パターンを使用しようとします。私が工場を作成した理由は、私が6つのサブクラスのピースを持っているため、メソッドのタイプのサブクラスをメソッドの引数として渡すことで、メソッドを使用してそれらをインスタンス化することではありません。
解決
あなたは反射的にaを作成しようとしています Piece
物体。
Class.forName()
スロー ClassNotFoundException
, 、 その間 Class.newInstance()
InstantiationException、Illegalaccessexceptionを投げる(したがって、なぜ投げる必要があるのか Throwable
.
クラスタイプを介してオブジェクトを作成するより良い方法は、おそらく次のことを行うことです。
class PieceFactory {
public Piece createPiece(String pieceType) throws Throwable{
Piece piece = null;
if ("SubPiece1".equals(pieceType)) {
piece = new SubPiece1();
} else if ("SubPiece2".equals(pieceType)) {
piece = new SubPiece2();
}
return piece;
}
}
詩, 、それはテストされていない、それを行うためのより良い方法を示しているだけです。
お役に立てれば! :)
他のヒント
両方 @SuppressWarnings
と throws Throwable
リングアラームベル。見る 効果的なJava ブロッホによって。
スローを宣言したくない場合は、どこでもブロックを試してみてください。
クラスを作成して拡張します RuntimeException
クラスまたは使用 RuntimeException
それ自体または関連 RuntimeException
拡張クラス。
次に、トライキャッチブロックをルート(工場クラスのメソッド)に配置し、ランタイム例外タイプにスローされた例外をラップします(上記のいずれか、どちらの場合でも)。
しかし、それは本当にあなたが必要とするものに依存します。ある時点では、まだ例外を処理する必要があります。そうしないと、アプリケーションが実行時に例外をスローし、アプリはそれらを処理しないと予想されることを実行しません。
try{
"your code which throws exception"
} catch(ThrownExceptionType e){
throw new RuntimrException(e);
}
まだ例外を処理する必要があることを忘れないでください。これがうまく機能するという意味ではありません。
例外はどこかにキャッチする必要があります。私が正しく理解した場合、あなたはこの工場に包まれている1つのクラスがあります。 FactoryWrapper
. 。コードのどこかでそのラッパークラスを使用し、それを使用している方法はおそらく(おそらく基本クラスで)トライ/キャッチブロックによっておそらく囲まれているため、例外をスローするか、キャッチすることができます。他の場所(あなたがあなたを渡している FactoryWrapper
参照)はおそらく最後の手段であり(それは良い練習ではありませんが、決して呼ばれない方法である可能性があります)、例外を捕まえる必要があります。
これは、他のクラスで例外を投げることができない理由についての可能な説明にすぎません。他の人がすでに述べたように、それは他の選択肢よりもはるかに遅いので、反射を使用しないようにしてください。
1つの抽象的な工場、次に各タイプに工場を使用できます。詳細を見る ここ と ここ 。お役に立てれば。
編集 :
ここ リフレクションに関する興味深い記事です。いつ使用するかアイデアが表示されます。
例外ハンドラーの作成に慣れることに取り組むことをお勧めします。それ以外の場合、あなたの唯一のオプションはあなたのコードをなぞります throws
あなたがすでに見たように、迷惑になり、困難を引き起こす宣言。
この場合、メソッドの本文は、2つのチェックされた例外をスローできます。 ClassNotFoundException
から forname()
電話、そして InstantiationException
から newInstance()
電話。
これらを最適に処理する方法は、アプリケーションと好みによって異なります。別の答えで示唆されているように、1つの選択肢は、単にそれらをキャッチし、未確認の例外を投げることです。これは、アプリケーションが完了したらこれらの例外が発生しないことを期待する場合、賢明です。これらの例外のいずれかが発生した場合、構成エラーを示す可能性が高いため、これはおそらくここで取るアプローチです。
しかし、これらの例外が通常の使用法で合理的に発生する可能性があると考える理由がある場合は、それらをどのように処理したいかを考える必要があります。たとえば、ピースのサブクラス名がユーザーによってGUIに入力されている場合(おそらく、私は知っているが、ポイントを作るためだけに)、 ClassNotFoundException
名前を簡単に間違えた可能性があるため、はるかに可能性が高くなります。そのような状況では、この方法がその例外をスローすることを許可し、発信者がそれをキャッチして処理することを要求することは理にかなっているかもしれません(例えば、要求されたクラスが存在しないというメッセージをユーザーに提供することにより)。
あなたのように反射を使用することは理想的ではありません。ピースクラスの名前を変更し、クライアントがハードコーディングされた完全に資格のあるクラス名を渡すとすぐに、アプリは壊れます。 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);
}
}
}
ここですべてを静的にして、例を自己完結型に保ちました。通常、フィールド、ピース、アブストラクトピース、ルークはトップレベルのモデルクラスであり、断片的なものもトップレベルです。
これは、私が思うあなたの例に行くためのより良い方法であり、例外処理の問題を回避します。
それに戻って、あなたが考慮できるいくつかのアプローチがあります(あなたの反射アプローチに基づいて):
あなたがしたように投げることができるように投げることは、すべてのエラーをまとめて、クライアントにとって非常に面倒で透明ではないエラー処理を行うため、悪い練習です。他に選択肢がない限り、それをしないでください。
すべてのチェックされた例外について、メソッドを「スロー」と宣言します。これが有効かどうかを判断するには、クライアントがスローしている例外タイプを知って理解する必要があるかどうかを検討してください。あなたの例では、Rookを作成したいクライアントは、あなたの反射コードによってスローされたInstantiationException、Illegalaccessexception、ClassNotFoundexceptionを知って理解する必要がありますか?おそらくこの場合はそうではありません。
クライアントが捕まえる必要はないランタイム例外でそれらを包みます。これは必ずしも良い考えではありません。あなたが呼び出しているコードがチェックされた例外をスローすることであるという事実には、通常、理由があります。あなたの例では、あなたが反映していたので、これは多くの方法で間違っている可能性があります(APIは、LinkageError、ExceptionInitializerError、ClassNotFoundException、Illegalaccessexception、InstantiationException、およびSecutionExceptionを宣言します)。ランタイム例外でチェックされた例外をラッピングしても、その問題はなくなりません。これを「コードの匂い」と考えています。エラーが回復不可能なシステム障害を意味する場合、それは有効な選択かもしれませんが、ほとんどの場合、そのような障害をより優雅に処理したいと思うでしょう。
完全なサブシステムのカスタムチェックされた例外をスローします。たとえば、springのorg.springframework.dao.dataaccessexceptionを参照してください。これは、クライアントが1つの例外タイプのみをキャッチする必要があり、必要に応じて詳細を把握できることを意味します。あなたの場合、チェック済みの断片化されたexceptionを作成し、それを使用してすべての反射エラーをラップすることができます。これは有効な例外ハンドリングパターンですが、あなたの断片を重視するために少し手を差し伸べるかもしれないと思います。
nullを返します。コード内のすべての例外をキャッチし、発生した場合にnullを返すだけです。 Javadocがこれを明確に示していることを確認してください。このアプローチの欠点は、クライアントがどこでもヌルをチェックする必要がある可能性があることです。
特定のエラータイプを返します。これは、しばらく前にJava Core APIで見たオタク(非常にオブジェクト指向の)アプローチです(Darn、覚えていない場所)。このために、追加のピースタイプを作成します。
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
. 。そうでない場合、ピースメソッドが呼び出されたときにスローされたUnsportedoperationExceptionに遭遇します。少し重いですが、非常に明示的です。ここまで行くかどうかはわかりませんが、それでも興味深いアイデアです。