34 votes

Comment ne pas répéter le code dans les blocs catch?

Je vais avoir du mal à ne pas me répéter dans un programme Java, je travaille en ce moment.

Dites, j'ai besoin de déclarer un grand nombre de méthodes qui, fondamentalement, sont structurés de la manière suivante:

public SomeEntity doSomething (String someAttribute, String anotherAttribute) {
    EntityManager em = this.createEntityManager();

    EntityTransaction tx = null;
    try {

        /*
         * ... independent logic ...
         */

        tx = em.getTransaction();
    } catch (RuntimeException e) {
        if (tx != null && tx.isActive()) { 
            tx.rollback();
        }
        throw e;
    } finally {
        em.close();
    }

    return something;
}

Le corps de la méthode de toutes les méthodes doit contenir les thèses des éléments pour la gestion des ressources.

Les "indépendants" logique elle-même sera assez complexe, afin de mettre l'instruction try/catch dans une méthode distincte de ne pas vraiment travailler.

Je veux éviter de répéter ce code. Quelles sont les meilleures pratiques à appliquer dans ces situations?

35voto

nos Points 102226

Créer une interface:

 public interface EntityManagerAction {
   public void execute(EntityManager em);
}
 

Et une classe utilitaire:

 public class EntityUtil {
  public static void executeWithEntityManager(EntityManagerAction action) {
    EntityManager em = someHowCreateEntityManager();

    EntityTransaction tx = null;
    try {
        action.execute(em);
        tx = em.getTransaction();
    } catch (RuntimeException e) {
        if (tx != null && tx.isActive()) { 
            tx.rollback();
        }
        throw e;
    } finally {
        em.close();
    }
  }
}
 

Vous pouvez maintenant réutiliser la plaque de la chaudière dans la classe EntityUtil et votre code devient:

 public SomeEntity doSomething (String someAttribute, String anotherAttribute) {
   Something something; 
   EntityUtil.executeWithEntityManager(new EntityManagerAction() {
        public void execute(EntityManager em ) {

        /*
         * ... independent logic ...
         */
         //use the passed in 'em' here.
        }
    });

    return something;
}
 

Voir aussi Qu'est-ce que l'idiome "Execute Around"?

8voto

eitanfar Points 2491

Si tous vos finally clauses sont utilisées pour fermer Streams et tels (tout ce qui implémente AutoCloseable), vous pouvez utiliser try-with-resources (comme suggéré dans un des commentaires) pour se débarrasser de l' finally de la clause.

Toutefois, si vous avez besoin d'une solution générique, et ont le même type d' Exceptions pris et le même genre de manipulation dans l' finally clause, vous pouvez créer une classe abstraite tels que:

abstract class SensitiveBlockHandler {
    public void handle() {
        try {
            doHandling();
        } catch (SomeException | AnotherException e) {
            // TODO: handle exceptions here ...
        } finally {
            // TODO: cleanup here ...
        }
    }

    protected abstract void doHandling();
}

Ensuite, vous pouvez créer des classes internes pour gérer les différentes situations, que ce soit anonyme ou non des cours. Le code devrait ressembler à quelque chose comme:

public SomeEntity doSomething (String someAttribute, String anotherAttribute) {
    new SensitiveBlockHandler() {
        protected void doHandling() {
            /*
             * ... independent logic ...
             */
        }
    }.handle();

    return something;
}

3voto

Jan Zyka Points 5808

Je voudrais créer une abstraction de l'indépendant logique, dire Job, et l' doSomething() deviendra processJob() en Service classe. Le vous appeler à votre processJob() pour chaque traitement et la tout le code de votre exemple, à l'exception de l' independent logic sera écrit exactement une fois.

Edit: ce Qui est ce que la nsa a suggéré dans un commentaire: qu'est-Ce que la "Exécuter Autour de" l'idiome?

3voto

Ben Green Points 978

Nous avons récemment été confrontés à un problème de ce type et avons décidé d’utiliser le modèle de conception de rappel .

Ainsi, si toutes les méthodes ont un code similaire et que seule la logique indépendante soit différente, vous pouvez créer une interface dont les implémentations gèrent le code indépendant. Donc, quelque chose comme ça:

 public SomeEntity doSomething (String someAttribute, String anotherAttribute) {
       return (SomeEntity)callbackMethod(someAttribute, anotherAttribute, new IndependentCodeInterfaceImpl1());
}

public SomeOtherEntity doSomethingElse (String someAttribute, String anotherAttribute) {
    return (SomeOtherEntity)callbackMethod(someAttribute, anotherAttribute, new IndependentCodeInterfaceImpl2());
}

private Object callbackMethod(String someAttribute, String anotherAttribute, IndependentCodeInterface independent) {
    EntityManager em = this.createEntityManager();
    EntityTransaction tx = null;
    Object response = null;
    try {
        response = independent.execute(someAttribute, anotherAttribute, em);
        tx = em.getTransaction();
    } catch (RuntimeException e) {
        if (tx != null && tx.isActive()) { 
            tx.rollback();
        }
        throw e;
    } finally {
        em.close();
    }
    return response;
}
 

3voto

Steve C Points 2199

Si vous utilisez un serveur d'applications JavaEE, votre code peut être considérablement simplifié à l'aide d'un bean de session sans état:

 @Stateless
public class SomethingFactory {

    @PersistenceContext
    private EntityManager em;

    public SomeEntity doSomething (String someAttribute, String anotherAttribute) {
       /*
        * ... independent logic ...
        */
       return something;
    }

}
 

Le conteneur traitera pour vous toute la sémantique de la gestion des transactions.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X