2011-01-13 16 views
0

Ich bin nicht alle daran gewöhnt, Ausnahmen zu behandeln, deshalb werfe ich sie nur, aber überall, wo ich eine Methode verwende, die diese Fabrik verwendet, sagt es mir, dass ich Ausnahmen wie werfen werfen muss.Ist mit meiner Factory-Klasse etwas nicht in Ordnung?

Zum Beispiel habe ich in einer meiner Klassen eine Methode, die viele Objekte mit der Methode, die die Fabrik verwendet, instanziiert. Ich kann die Methode in dieser Klasse verwenden, indem ich einfach die Ausnahme ausspreche, aber es wird nicht funktionieren, wenn ich versuche, einen Verweis auf diese Klasse an eine andere Klasse weiterzuleiten und dann die Methode von dort zu verwenden. Dann zwingt es mich zu versuchen, die Ausnahme zu fangen.

Ich brauche wahrscheinlich keine Fabrik, aber es schien interessant und ich würde gerne versuchen, Muster zu verwenden. Der Grund, warum ich die Factory erstellt habe, war, dass ich 6 Unterklassen von Piece habe und ich eine Methode verwenden möchte, um sie zu instanziieren, indem ich den Typ der Unterklasse, die ich möchte, als Argument an die Methode übergebe.

+0

Warum behandeln Sie Ihre Ausnahmen nicht in den Zeilen, die sie in 'createPiece' erzeugen? – justkt

+1

Viel besser, Reflexion zu vermeiden. –

+0

Danke allen! Ich habe meinen Code ein wenig geändert und dieses Mal keine Reflektion benutzt. –

Antwort

1

Sie versuchen, ein Piece Objekt reflektiv zu erstellen.

Class.forName() wirft ClassNotFoundException, während Class.newInstance() wirft InstantiationException, Illegal (daher, warum Sie Throwable werfen müssen

Eine bessere Möglichkeit, ein Objekt durch Klassentypen erstellen, ist wahrscheinlich durch folgende Maßnahmen:.

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;  
    } 
} 

PS, es ist ungetestet, zeigt nur einen besseren Weg, es zu tun

Ho pe das hilft! :)

+1

Ihre Vorschläge erfordern das Bestehen einer Klasse. Wie soll der aufrufende Code das Class-Objekt an erster Stelle erhalten? –

+0

Danke! Entschuldigung, ich bin nicht wirklich an die Notationen gewöhnt :). Was macht gemein? und ? –

+0

@Dave Costa, wahr .... gut entdeckt, ich las seine Spezifikation und binde von dort codiert. Ich werde diesen Beitrag aktualisieren. –

1

Wenn Sie nicht möchten, Würfe Aussagen zu erklären oder versuchen/Block überall zu blockieren.

Erstellen Sie eine Klasse und RuntimeException Klasse oder RuntimeException selbst oder verwandten RuntimeException erstreckt Klassen verwenden, verlängern.

Dann try-catch-Block an den Stamm (Ihre Factory-Klassen-Methoden) und wickeln Sie die Ausnahmen in einen Runtime-Ausnahme-Typ (eine der oben genannten, je nachdem, was Sie wählen).

Allerdings hängt es wirklich davon ab, was Sie brauchen. Irgendwann müssen Sie noch Ausnahmen behandeln oder Ihre Anwendung wird zur Laufzeit eine Ausnahme auslösen, und die App wird nicht tun, was erwartet wurde, wenn Sie sie nicht behandeln.

try{ 
    "your code which throws exception" 
} catch(ThrownExceptionType e){ 
    throw new RuntimrException(e); 
} 

Denken Sie daran, dass Sie die Ausnahme noch verarbeiten müssen. Es bedeutet nicht, dass das alles gut funktionieren wird.

+1

Richtig, danke! Wie wickle ich die Ausnahmen obwohl? :) Ich verstehe nicht wirklich, warum die Ausnahmen in erster Linie da sind, sollte es nicht funktionieren, um Objekte auf diese Weise zu erstellen? Ich meine, was schreibe ich in den Try-Catch-Block? –

+0

@Alex Siehe oben, bearbeitet den Beitrag. Ausnahme sind für die Ausnahmefälle. Computer kann nicht entscheiden, was automatisch zu tun ist, wenn die zu ladende Klasse "Class.forName (pieceType)" nicht gefunden werden kann. Sie müssen ihm auch in Ausnahmefällen sagen, was zu tun ist. – fmucar

+0

@Alex: Ausnahmen sind gut (nur ein Beispiel) für Probleme, die Sie nicht vorhersehen können, wie wenn die Datei, auf die Sie zugreifen möchten, nicht existiert oder etwas Ähnliches. Es ist eine gute Möglichkeit, den Ausführungsfluss zu stoppen und nicht zu versuchen, etwas zu tun, was nicht möglich ist, es ist fehlerhaft, daher wird die Ausführung im try/catch-Block fortgesetzt. – Atticus

1

Sowohl @SuppressWarnings als auch throws Throwable Ringalarmglocken. Siehe Effective Java von Bloch.

+0

Ich habe davon gehört, es scheint gut zu sein. Ich muss einfach zuerst die Grundlagen besser lernen :). Aber sollte dies nicht eine gültige Methode zum Erstellen von Objekten sein? Warum die Ausnahmen? –

1

Ausnahmen müssen irgendwo gefangen werden. Wenn ich richtig verstanden habe, hast du eine Klasse, die um diese Fabrik gewickelt ist, sagen wir FactoryWrapper. Irgendwo in Ihrem Code benutzen Sie diese Wrapper-Klasse und Sie können entweder Exception werfen oder sie fangen, weil die Methode, wo Sie sie benutzen, wahrscheinlich irgendwann (in einer Basisklasse wahrscheinlich) von einem try/catch-Block umgeben ist anderer Ort (an den Sie Ihre FactoryWrapper Referenz weitergeben) ist wahrscheinlich ein letzter Ausweg (es ist keine gute Übung, aber es könnte eine Methode sein, die nie aufgerufen wird), wo die Ausnahme abgefangen werden muss.

Dies ist nur eine mögliche Erklärung, warum Sie die Ausnahme in Ihrer anderen Klasse nicht werfen können. Wie andere schon erwähnt haben, versuchen Sie nicht mit Reflektion, weil das viel langsamer ist als die anderen Alternativen.

Sie könnten eine abstrakte Fabrik und dann eine Fabrik für jeden Typ verwenden. Siehe Details here und here. Hoffe das hilft.

EDIT:

Here ist ein interessanter Artikel über Reflexion. Es gibt Ihnen eine Idee, wann Sie es verwenden können.

+0

Danke! Die Factory befindet sich in der Klasse Piece, in der ich auch eine Methode verwende, die Factory verwendet. Ist die Stückklasse dann der Wrapper? Also alles andere als die Verwendung von Reflektion ist wahrscheinlich eine bessere Alternative? –

+0

@Alex: Ja, ich habe Ihre Stückklasse als Wrapper bezeichnet. Ich würde nur darüber nachdenken, ob es wirklich wichtig ist. Ich habe meine Antwort mit einem Link aktualisiert, in dem erläutert wird, wann Reflektionen verwendet werden sollen. – Atticus

1

Ich empfehle, dass Sie daran arbeiten, sich mit dem Schreiben von Exception-Handlern vertraut zu machen. Ansonsten besteht die einzige Möglichkeit darin, Ihren Code mit throws Deklarationen zu rätseln, was ärgerlich wird und Schwierigkeiten verursacht, wie Sie bereits gesehen haben.

In diesem Fall kann der Körper Ihrer Methode zwei Ausnahmen geprüft werfen: ClassNotFoundException vom forname() Anruf und InstantiationException vom newInstance() Anruf.

Wie Sie diese am besten behandeln, hängt von Ihrer Anwendung und Ihren Vorlieben ab. Wie von einer anderen Antwort vorgeschlagen, besteht eine Option darin, sie einfach abzufangen und unkontrollierte Ausnahmen zu werfen. Dies ist sinnvoll, wenn Sie davon ausgehen, dass diese Ausnahmen nie auftreten sollten, sobald die Anwendung abgeschlossen ist. Dies ist wahrscheinlich der Ansatz, den ich hier anstellen würde, denn wenn eine dieser Ausnahmen auftritt, würde dies wahrscheinlich auf einen Konfigurationsfehler hinweisen.

Aber wenn es Grund zu der Annahme gibt, dass diese Ausnahmen bei normaler Verwendung sinnvoll auftreten können, sollten Sie darüber nachdenken, wie Sie mit diesen Ausnahmen umgehen möchten. Zum Beispiel, wenn der Name der Stück-Unterklasse von einem Benutzer in eine GUI eingegeben wurde (nicht wahrscheinlich, ich weiß, aber nur um den Punkt zu machen), wird eine ClassNotFoundException viel wahrscheinlicher, da der Name leicht falsch geschrieben werden könnte. In dieser Situation kann es sinnvoll sein, dieser Methode zu erlauben, diese Ausnahme zu werfen und den Aufrufer zu veranlassen, sie zu fangen und zu behandeln (z. B. indem sie dem Benutzer eine Nachricht zurückgibt, dass die angeforderte Klasse nicht existiert).

0

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.