Question

class PieceFactory {     
     @SuppressWarnings("rawtypes")
     public Piece createPiece(String pieceType) throws Throwable{
        Class pieceClass = Class.forName(pieceType);
        Piece piece = (Piece) pieceClass.newInstance();

         return piece;       
     }
}

Je ne suis pas tous utilisés pour la gestion des exceptions mais donc je les jeter, mais partout où j'utilise une méthode qui utilise cette usine, il me dit que je dois lancer des exceptions comme throwable.

Par exemple, dans une de mes classes, j'ai une méthode qui instancie beaucoup d'objets à l'aide de la méthode qui utilise l'usine. Je peux utiliser la méthode dans cette classe par jeter de l'exception, mais il ne fonctionnera pas si je tente de passer une référence à cette classe à une autre classe, puis utiliser la méthode à partir de là. Ensuite, il me force à essayer de rattraper l'exception.

Je ne doute pas besoin d'une usine, mais il a semblé intéressant et je voudrais essayer de modes d'utilisation. La raison pour laquelle j'ai créé l'usine était que j'ai 6 sous-classes de pièces et je wan't d'utiliser une méthode pour les instancier en passant le type de sous-classe que je veux comme un argument à la méthode.

Était-ce utile?

La solution

Vous essayez de créer un objet pensivement Piece.

Class.forName() jette ClassNotFoundException, tandis que Class.newInstance() jette InstantiationException, IllegalAccessException (donc pourquoi vous avez besoin de jeter Throwable.

Une meilleure façon de créer un objet à travers les types de classe est probablement en procédant comme suit:

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 , il est non testé, juste montrer une meilleure façon de le faire.

Hope this helps! :)

Autres conseils

Les deux cloches d'alarme anneau @SuppressWarnings et throws Throwable. Voir Effective Java Bloch.

Si vous ne voulez déclarer lance des déclarations ou bloc try / catch partout.

Créer une classe et étendre la classe de RuntimeException ou de l'utilisation elle-même RuntimeException ou RuntimeException connexes extension des classes.

Ensuite, lieu bloc try-catch à la racine (vos méthodes de classe d'usine) et envelopper les exceptions lancées dans un type d'exception d'exécution (un des ci-dessus, selon votre choix).

Cependant, cela dépend vraiment de ce que vous avez besoin. À un certain moment, vous aurez toujours besoin d'exceptions poignée ou votre application jetterez exception à l'exécution et l'application ne serez pas ce qui était attendu si vous ne les manipulez.

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

Rappelez-vous toujours besoin de gérer l'exception. Cela ne signifie pas que ça va marcher tout bien.

Les exceptions doivent être pris quelque part. Si je comprends bien, vous avez une classe qui est enroulé autour de cette usine, par exemple un FactoryWrapper. Quelque part dans votre code que vous utilisez cette classe d'emballage et vous pouvez soit vous jeter exception ou l'attraper parce que la méthode où vous utilisez est probablement entouré à un moment donné (dans une classe de base probablement) par un bloc try / catch alors que la autre endroit (à laquelle vous passez votre référence FactoryWrapper) est probablement un dernier recours (il est pas une bonne pratique, mais il pourrait être une méthode qui est jamais appelé) où la nécessité d'exception à prendre.

Ceci est juste une explication possible de la raison pour laquelle vous ne pouvez pas jeter l'exception dans l'autre classe. Comme d'autres l'ont déjà dit, essayez de ne pas utiliser la réflexion parce que c'est beaucoup plus lent que les autres alternatives.

Vous pouvez utiliser une usine abstraite et une usine pour chaque type. Voir les détails et ici . Espérons que cela aide.

EDIT:

Voici un article intéressant sur la réflexion. Il vous donnera une idée quand l'utiliser.

Je vous recommande de travailler à obtenir à l'aise avec l'écriture de gestionnaires d'exception. Sinon, votre seule option est de cribler votre code avec des déclarations de throws qui devient ennuyeux difficultés et les causes, comme vous l'avez déjà vu.

Dans ce cas, le corps de votre méthode peut lancer deux exceptions vérifiées:. ClassNotFoundException de l'appel forname() et InstantiationException de l'appel newInstance()

Comment mieux gérer ces dépend de votre application et vos préférences. Comme l'a suggéré une autre réponse, une option est de les attraper simplement et lancer des exceptions non vérifiées. Ceci est raisonnable si vous attendez que ces exceptions ne devraient jamais se produire une fois que la demande est complète. Ceci est probablement l'approche que je prendrais ici, car si l'une de ces exceptions ont eu lieu, il serait probablement indiquer une erreur de configuration.

Mais s'il y a des raisons de penser que ces exceptions pourraient se produire raisonnable dans un usage normal, vous devriez penser à la façon dont vous voulez les gérer. Par exemple, si on est entré étant le nom de la pièce dans une interface graphique par un utilisateur (peu probable, je sais, mais juste pour faire le point), puis un ClassNotFoundException devient beaucoup plus probable que le nom pourrait facilement être mal orthographié. Dans cette situation, il peut être judicieux de permettre à cette méthode pour lancer cette exception, et exiger de l'appelant pour attraper et manipuler (par exemple en fournissant un retour de message à l'utilisateur que la classe demandée n'existe pas).

En utilisant la réflexion comme vous est pas idéal. Dès que vous renommez une classe de pièces et les clients passent Hardcoded entièrement qualifiés-classnames, votre application se brisera. La suggestion de l'Elite Gent évite ce problème, mais nécessite encore des clients de connaître la classe concrète, ce qui est exactement ce que le modèle usine essaie de se cacher. Une meilleure approche à mon avis, serait d'utiliser soit un ENUM pour représenter les types de pièce, et laisser passer le client comme argument de votre méthode d'usine, ou créer des méthodes d'usine distinctes pour chaque type (avec 6 types de pièces qui est possible).

Depuis que je suis tergiverser quand même, voici un exemple de l'approche ENUM:

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

J'ai fait tout statique ici pour garder l'exemple autonome. Normalement, le terrain, Piece, AbstractPiece et Rook serait classes de modèle de niveau supérieur et PieceFactory serait de haut niveau aussi.

Ceci est une meilleure façon d'aller pour votre exemple, je pense, et évite le problème de gestion des exceptions.

De retour à cela, il y a deux approches que vous pouvez prendre en compte (en fonction de votre approche de réflexion):

Lancer Throwable comme vous l'avez fait est une mauvaise pratique puisqu'ils amalgament toutes les erreurs et rend la gestion des erreurs très lourdes et manque de transparence pour les clients. Ne pas faire à moins que vous n'avez pas d'autres options.

Declare « lance » sur votre méthode pour toutes les exceptions vérifiées. Pour décider si cela est valable, pensez si le client doit connaître et comprendre le type d'exception que vous jetez. Dans votre exemple, si le client qui veut créer un Rook connaître et comprendre les InstantiationException, IllegalAccessException et ClassNotFoundException jeté par votre code de réflexion? Probablement pas dans ce cas.

Enveloppez-les dans une exception d'exécution qui ne sera pas nécessairement pris par les clients. Ce n'est pas toujours une bonne idée. Le fait que le code que vous appelez est de lancer des exceptions vérifiés a généralement une raison. Dans votre exemple, vous faisiez la réflexion, et cela peut aller mal à bien des égards (l'API déclare LinkageError, ExceptionInInitializerError, ClassNotFoundException, IllegalAccessException, InstantiationException et SecurityException). Enroulant les exceptions vérifiées dans une exception d'exécution ne fait pas ce problème disparaîtra. Je considère que ce faisant, une «odeur de code. Si l'erreur signifie une défaillance du système irrécupérables, alors il pourrait être un choix valable, mais dans la plupart des cas, vous ne voulez traiter plus ce genre de problèmes.

Jeter une exception contrôlée personnalisée pour un sous-système complet. Voir pour le org.springframework.dao.DataAccessException exemple de printemps qui est utilisé pour envelopper toutes les exceptions d'accès aux données spécifiques de mise en œuvre. Cela signifie les clients devront prendre un seul type d'exception et peut comprendre les détails si elles doivent. Dans votre cas, vous pouvez créer un PieceCreationException vérifié et l'utiliser pour envelopper toutes les erreurs de réflexion. Ceci est un modèle de gestion des exceptions valables, mais je pense que ce serait peut-être un peu pénalisantes pour votre PieceFactory.

Retour null. Vous pouvez attraper toutes les exceptions dans votre code et simplement retourner null si elles se produisent. Assurez-vous que votre JavaDoc indique clairement. Un inconvénient de cette approche est que les clients pourraient devoir vérifier partout nulls.

Retour un type d'erreur spécifique. C'est une approche que je geeky de scie (très orienté objet) dans l'API Java de base quelque part un certain temps (zut, ne me souviens pas où). Pour cela, vous devez créer un type supplémentaire 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");
    }
}

Et le retour d'une instance de ce type lorsqu'une erreur se produit lors de la création. L'avantage est qu'il est plus autodocumenté que de retourner une simple valeur nulle, mais les clients devrait évidemment de vérifier cela en utilisant quelque chose comme instanceof. Si elles ne le font pas, ils rencontreront le UnsupportedOperationException jeté lorsque les méthodes de pièce sont appelés. Un peu lourd, mais très explicite. Je ne suis pas sûr que j'aller jusque-là, mais il est encoreune idée intéressante.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top