Je ne suis pas tout à fait sûr du terme officiel pour ce type de schéma/problème, mais voici ce à quoi je suis confronté :
J'ai une opération qui est assez importante. Elle peut soit réussir, soit échouer. Chaque réussite ou échec entraîne soit le résultat de l'opération réussie, soit des informations sur la raison de l'échec de l'opération. J'ai du mal à architecturer cette fonction "correctement".
class Pass{
int someGoodStuff;
double someOtherGoodStuff;
}
class Fail{
String failureMsg;
float howMuchOperationWasOffBy;
}
class Operator{
public ??? operation(){
}
}
Approche 1 : Les états d'échec sont comme des exceptions. Il faut les lancer. Cela me permet d'inclure l'information sur l'échec et de faire en sorte que le type de retour soit simplement Pass. Cependant, ces états d'échec ne sont pas des erreurs de langage de programmation, mais des erreurs de logique commerciale. Cette approche me pose deux problèmes : d'une part, elle confond les échecs de la logique d'entreprise avec les erreurs Java réelles (ce qui semble erroné) et d'autre part, elle détourne le flux d'exécution normal sans raison valable.
Approche 2 : Les fonctions en java aiment retourner un seul type d'objet, donc que Pass et Fail implémentent tous les deux une interface Result, et que le type de retour de la fonction soit celui-là.
interface Result{...}
class Pass implements Result{...}
class Fail implements Result{...}
class Operator{
public Result operation(){...}
}
Cependant, les états de réussite et d'échec que la fonction renvoie sont complètement différents. Ils n'ont que peu ou pas de variables ou de fonctions qui se chevauchent. Cela semble erroné et me réduit à devoir instanceof
toutes les informations importantes des réussites et des échecs.
Approche 3 : Une sorte d'objet d'union qui peut être soit un succès, soit un échec (mais pas les deux).
class Result{
public Pass pass=null;
public Fail fail=null;
}
class Operator{
public Result operation(){...}
}
Cela a l'avantage que je peux dire des choses comme
Result result=op.operation();
if (result.succeed()){
doStuffWithPass(result.getPass());
}else{
doStuffWithFail(result.getFail());
}
C'est en gros ce que je faisais avec instanceof, mais en plus joli ; le code ressemble maintenant à ce que l'on pourrait attendre de lui. Il est clair à suivre.
Cependant, Java ne dispose pas de véritables types d'Union. Je dois m'assurer que personne n'essaie accidentellement de manipuler les variables pass d'un Result fail ou vice versa. De plus, toutes les méthodes que j'appelle sur le type union doivent être prédites pour déterminer s'il s'agit d'un succès ou d'un échec (cette branche If doit soudainement être présente partout).
Enfin, bien que ce ne soit pas encore un réel problème, je pense qu'un tel modèle alloue de l'espace à la fois pour un succès et un échec pour chaque résultat, alors que je sais qu'il ne peut s'agir que de l'un d'entre eux (c'est-à-dire qu'il devrait occuper un espace égal à Max(space(Pass),space(Fail))
)
Aucune de ces approches ne semble parfaite. J'ai l'impression qu'il devrait y avoir un modèle pour résoudre ce genre de problème. En existe-t-il un ?