web-dev-qa-db-fra.com

Comment renvoyer des avertissements avec le résultat du calcul à l'appelant d'une méthode Java?

J'ai une classe, appelons-la Calculator, avec une méthode comme celle-ci:

public double[] performCalculation(double[] someInData)

Cette méthode peut générer un certain nombre d'avertissements non fatals (représentés sous la forme d'un tableau de chaînes avec des noms d'avertissement, tels que InDataOutOfBounds). Quelle est la façon la plus élégante et javaesque de renvoyer ces avertissements à l'appelant? Je vois quelques possibilités, aucune que j'aime particulièrement:

  1. Renvoie une instance d'une classe personnalisée contenant à la fois le résultat du calcul et le tableau des avertissements.
  2. Laissez performCalculation accepter un argument supplémentaire String[] warnings Qu'il remplit avec les avertissements. Cela pourrait être un non, car il semble que je ne puisse pas redimensionner le tableau passé, et il n'y a aucun moyen de savoir à l'avance combien d'avertissements il y aura.
  3. Conservez performCalculation tel quel, mais ajoutez une méthode String[] getWarnings() à Calculator, en renvoyant les avertissements du dernier calcul.

EDIT: Je connais cette question. Je dirais que ce n'est pas un doublon, car la première réponse à cette question indique que la réponse dépend de la situation. Il s'agit d'un cas plus spécifique, qui peut obtenir une réponse plus spécifique.

27
Anders
  1. Oui. Faites ça. C'est Java, n'ayez pas peur des cours. C'est à ça que servent les cours.

  2. Vraiment mauvais. C'est légèrement moins mauvais avec un Collection au lieu d'un tableau, mais vous demandez toujours une sémantique ad-hoc mal spécifiée qui ne sera jamais utilisée correctement.

  3. Peut fonctionner si vous êtes absolument, positivement certain que vous n'utiliserez jamais Calculator sauf dans une situation de contrôle unique. Malheureusement, la parallélisation des choses est l'un des changements les plus courants requis après coup.

50
Kilian Foth

Votre option 1 est la meilleure solution, je pense. Il renvoie tous les résultats, y compris les avertissements, dans un objet personnalisé. Vous ne maintenez pas l'état dans l'objet effectuant votre appel de méthode, ce qui signifie que vous n'avez pas à vous soucier de conserver et de gérer votre état (par exemple dans un scénario fileté, ou même d'appeler la méthode deux fois de suite et de perdre des résultats par inadvertance) .

N'ayez pas peur de créer des objets personnalisés pour ce genre de chose. En plus de contenir les champs, vous pouvez également ajouter des méthodes à cet objet pour lier ces informations et rendre compte de l'état, par exemple returnResults.hasWarnings()).

Vous constaterez probablement que davantage de fonctionnalités tombent dans cet objet de retour. Si je fais des choses similaires (par exemple dans Scala), je commence souvent par retourner un Tuple (ensembles de résultats liés ensemble) et très rapidement le convertir en une nouvelle classe avec des fonctionnalités supplémentaires)

27
Brian Agnew

Une quatrième option, plutôt que de passer dans un String[] warnings (ou de manière plus flexible une mutable List<String> warnings) comme vous le suggérez dans # 2, serait de passer dans un écouteur de rappel ou un objet qui reçoit et affiche les avertissements. Pour des calculs rapides, il s'agit très probablement d'un excès de connaissances, mais cela peut être un moyen de procéder si votre calcul est long ou doit autrement commencer à afficher des notifications asynchrones. Si vous utilisez ce modèle, vous souhaiterez peut-être fournir une implémentation d'écoute sans opération et une implémentation d'écoute agrégée pour une inspection ultérieure, et envisager également de rendre l'écouteur annulable ou facultatif (via une surcharge).

public interface WarningListener {
  void warn(String warning);
  void showProgress(int percentage);
}

public Result performCalculation(Input input, WarningListener listener) { ... }

Cependant, à moins qu'il n'y ait une raison spécifique de rappeler le calcul plus tôt, j'irais avec l'option # 1: En l'absence de structures, une classe légère Java est la meilleure façon de renvoyer plusieurs valeurs comme une seule unité.

16
Jeff Bowman

Comme d'autres l'ont déjà souligné, le n ° 1 est votre meilleur pari.

Du côté de l'implémentation, vous voudrez peut-être jeter un œil au Modèle de notification

Une notification est "Un objet qui recueille des informations sur les erreurs et d'autres informations dans la couche de domaine et les communique à la présentation."

Donc, l'idée essentielle est de regrouper les erreurs (ou avertissements, si vous le souhaitez) dans un seul objet.

6
Lovis

Si l'appelant doit être en mesure de réagir aux avertissements, la méthode préférée est votre première proposition, c'est-à-dire renvoyer un type personnalisé (comme d'autres l'ont déjà souligné).

Cependant, la plupart du temps, l'appelant n'est pas intéressé par les avertissements. Pour ces cas, ces avertissements sont généralement envoyés à un enregistreur (voir par exemple slf4j ).

public double[] performCalculation(double[] someInData) {
    // ...
    if (dataOutOfRange) {
        LOGGER.warn("Input data {} is out of range!", data);
    }
    // ...
    return result;
}

Une autre façon serait de laisser l'appelant passer dans une sorte d'écouteur, en utilisant Java 8 expressions lambda.

public double[] performCalculation(double[] someInData, Consumer<Warning> warningConsumer) {
    // ...
    if (dataOutOfRange) {
        warningConsumer.accept(new Warning(WarningType.OUT_OF_RANGE, value));
    }
    // ...
    return result;
}
2
user3151902

J'ai utilisé une approche différente dans le passé. Dans mon scénario, j'avais un grand nombre de méthodes pouvant générer des avertissements. J'ai créé une classe Context, qui contient diverses choses, y compris une liste d'avertissements. Il possède également une méthode statique getCurrentContext ().

Donc, chaque fois que j'ai besoin de générer un avertissement, je peux faire:

Context.getCurrentContext().addWarning("Email address is longer than 64 characters; truncating");

L'avantage de cet arrangement est qu'un avertissement peut être créé n'importe où.

La principale difficulté est l'implémentation de getCurrentContext. Mon application était une application Web (un service REST/JSON), où chaque demande est traitée dans un thread séparé. Au début d'une demande, un contexte vide est créé et stocké en fonction de l'ID de thread. getCurrentContext utilise l'ID de thread actuel pour obtenir le contexte. À la fin de la demande, lorsque le JSON de sortie est construit, tous les avertissements sont inclus dans le JSON.

Dans votre scénario, si performCalculation est la seule méthode qui peut provoquer des avertissements, ce n'est probablement pas la solution pour vous.

Je serais intéressé à recevoir des commentaires sur cette approche générale. Cela fonctionne très bien pour cette application particulière - mais cela semble un peu hacky.

1
paj28

Je pense que l'option 1 est la meilleure, mais à mon avis, il y a une meilleure façon de le faire que de créer une classe de résultats personnalisée.

Utilisez simplement un 2Tuple, comme certains l'ont mentionné, du résultat et de la liste des avertissements.

public Tuple2<double[],String[]> performCalculation(double[] someInData){
    ...
}

Voici un exemple de mise en œuvre d'un Tuple

Je pense que c'est mieux car en général les classes devraient être réutilisables. Une classe de résultats que vous utilisez une fois pour obtenir ces résultats ne l'est pas. Mais un tuple est général et peut être réutilisé dans de nombreux cas.

1
Ryan Stull