Une façon de renvoyer plusieurs valeurs de retour à partir d'une méthode: mettez la méthode à l'intérieur de la classe représentant la valeur de retour. Est-ce un bon design?

15

J'ai besoin de renvoyer 2 valeurs d'une méthode. Mon approche est la suivante:

  1. créer une classe interne avec 2 champs qui seront utilisés pour conserver ces 2 valeurs
  2. mettre la méthode à l'intérieur de cette classe
  3. instancier la classe et appeler la méthode.

La seule chose qui sera modifiée dans la méthode est qu'à la fin, elle affectera ces 2 valeurs aux champs de l'instance. Ensuite, je peux aborder ces valeurs en faisant référence aux champs de cet objet.

Est-ce un bon design et pourquoi?

dhblah
la source
Une autre option (probablement mauvaise): voir BitInteger[] java.math.BigInteger.divideAndRemainder(BitInteger val). Il renvoie 2 entiers comme valeurs de retour dans un tableau.
earlNameless du
Quel type sont les deux valeurs que vous souhaitez renvoyer?
Tulains Córdova
Copie possible - stackoverflow.com/questions/12668186/… .
m3th0dman

Réponses:

15

Je dirais cela selon les lignes suivantes:

  • Pourquoi votre méthode renvoie-t-elle exactement plusieurs valeurs? De quel type de cohésion parlons-nous - ces valeurs devraient-elles en fait être des champs sur une seule classe, ou sont-elles simplement renvoyées par coïncidence par la même méthode, mais autrement sans rapport? Si c'est le dernier, vous voudrez peut-être plutôt envisager de diviser la méthode en deux méthodes. Modifier: utilisez votre jugement ici; parfois, un type de cohésion «fortuite» peut être la meilleure option. Une autre option consiste à utiliser une construction de paire ou de tuple, bien que dans la POO, ceux-ci ne sont généralement pas vus dans les API publiques (certaines exceptions notables étant les collections standard, etc.).
  • Si les valeurs méritent de former une classe, je déconseillerais probablement d'utiliser une classe interne. Les classes internes sont généralement utilisées comme détails d'implémentation internes, qui sont cachés de l'extérieur. Y a-t-il une raison pour laquelle ce résultat ne devrait pas être une classe à part entière, à part entière?
  • Outre la détention de données, quelles opérations sont applicables à cette nouvelle classe? Dans la conception orientée objet, vous souhaitez que le comportement associé soit proche des données pertinentes (qui semblent également être vos intentions). La méthode à laquelle vous faites référence ne devrait-elle pas plutôt vivre dans cette classe?

Pour résumer, je verrais si je peux transformer cet "objet de données" en une classe à part entière avec à la fois des données et un comportement. Comme commentaire supplémentaire, vous souhaiterez peut-être rendre la classe immuable, car son état est défini une fois. Le rendre immuable aidera à éviter qu'il ne soit mal défini ou modifié ultérieurement (par exemple, quelqu'un définissant l'un des champs sur null et le transmettant).

Edit: Comme Patkos Csaba le souligne correctement, le principe appliqué ici est le principe de responsabilité unique ( SRP ) - la classe que vous essayez de créer devrait vraiment avoir une responsabilité (définie comme une raison de changement ). Cette directive de conception devrait vous aider à déterminer si vos deux champs appartiennent à une seule classe ou non. Pour s'en tenir à l'exemple de Wikipedia, votre classe pourrait être considérée comme un type de rapport, auquel cas il est conforme à SRP, mais il est difficile de commenter sans plus d'informations.

Daniel B
la source
Bien que je sois d'accord avec l'idée générale de cette réponse, il existe des cas légitimes où deux éléments de données étroitement liés sont calculés ensemble, mais il est inutile de les lier autrement ailleurs dans le programme. Dans un tel cas, il peut être suffisamment propre pour renvoyer quelque chose comme Pair<OneClass, AnotherClass>. Certaines personnes seraient en désaccord . Dans tous les cas, Pairdevrait être un détail d'implémentation et ne jamais apparaître dans une méthode d'API publique.
9000
@ 9000 je suis d'accord; En fait, je voulais dire que le mot considérait être pris à la lettre dans ce cas, c'est-à-dire que la division de la méthode n'est pas toujours la meilleure solution, c'est juste une indication approximative dans cette direction. Je ferai un montage dans ce sens.
Daniel B
1
Bonne réponse. La seule chose que j'ajouterais est une référence au principe de responsabilité unique (PRS) ( en.wikipedia.org/wiki/Single_responsibility_principle ). Si elle est cochée, il est très possible que la méthode en discussion soit juste une simple violation de SRP et une scission soit une solution simple. D'après mon expérience, chaque fois qu'une méthode veut retourner 2 valeurs ou plus, dans 90% des cas, il y a 2 méthodes ou une autre classe doit être extraite.
Patkos Csaba
@PatkosCsaba merci, a fait une modification pour l'inclure. Je m'en tiens normalement à expliquer les choses en termes de couplage et de cohésion, mais je suppose que les principes SOLIDES sont considérés comme les règles de base à vivre de nos jours.
Daniel B
@DanielB: Je vois SOLID comme un niveau supérieur et probablement plus facile à comprendre les concepts. Le couplage et la cohésion sont toujours la référence, mais ils sont plus bas. SOLID fait un grand usage du couplage et de la cohésion pour expliquer ses principes et les présenter à un niveau plus générique.
Patkos Csaba
10

Il y a le concept d'un Tuple qui est présenté dans d'autres langages, comme Python.

On pourrait retourner une instance de cette classe générique qui est facilement réutilisable:

public class TypedTuple<L, R> implements Serializable {
private static final long serialVersionUID = 1L;

  protected L left;
  protected R right;

  protected TypedTuple() {
    // Default constructor for serialization
  }

  public TypedTuple(L inLeft, R inRight) {
    left = inLeft;
    right = inRight;
  }

  public L getLeft() {
    return left;
  }

  public R getRight() {
    return right;
  }
}
Cuga
la source
2
Il est parfois agréable d'avoir une méthode statique générique create(), pour éviter d'avoir à spécifier les paramètres de type dans le constructeur. De plus, je nommerais cette classe Pairplutôt que Tuple, étant donné qu'elle ne peut représenter que 2 tuples de valeurs.
augurar
5

Il semble que cette classe enlève des responsabilités à une autre classe, et cela me fait penser que ce design n'est pas génial.

Pour une méthode qui renvoie des valeurs multibles, je préfère

  • retourner un conteneur générique (par exemple une liste ou une carte) contenant les valeurs de retour

ou

  • créer une classe pour la valeur de retour, qui contient uniquement les champs nécessaires + les accesseurs + un constructeur avec tous les champs

Exemple pour la deuxième option:

public Class FooBarRetval {
   private String foo;
   private int bar;

   public FooBarRetval (String foo, int bar) {
      this.foo = foo;
      this.bar = bar;
   }

   public String getFoo() {
      return foo;
   }

   public int getBar() {
      return bar;
   }
}
user281377
la source
Rendre les champs publics ajoute la possibilité de modifier les valeurs séparément, bien que clairement les valeurs aient une relation (sinon elles n'auraient pas à être retournées par la même méthode). Je découragerais fortement ce modèle dans cette situation particulière. Mais le point principal est que la discussion entre les attributs publics et privés n'a rien à voir avec la question des PO et je ne vois aucune raison pour la dernière phrase d'une bonne réponse.
scarfridge
Le premier devrait être préféré.
Juanin
scarfridge: Point pris, dernière phrase supprimée.
user281377
2
Utilisez simplement les champs finaux publics, aucune raison de perdre du temps avec les accesseurs.
augurar
0

Réponse courte: vous pouvez renvoyer un tableau ou une liste avec les deux valeurs.

Personnellement, j'écrirais deux méthodes distinctes, comme

int x = obj.getX();
int y = obj.getY();
Tulains Córdova
la source