Un modo per restituire più valori di ritorno da un metodo: inserire il metodo all'interno della classe che rappresenta il valore di ritorno. È un buon design?


15

Devo restituire 2 valori da un metodo. Il mio approccio è il seguente:

  1. creare una classe interna con 2 campi che verranno utilizzati per mantenere quei 2 valori
  2. inserisci il metodo all'interno di quella classe
  3. creare un'istanza della classe e chiamare il metodo

L'unica cosa che verrà modificata nel metodo è che alla fine assegnerà quei 2 valori ai campi dell'istanza. Quindi posso indirizzare quei valori facendo riferimento ai campi di quell'oggetto.

È un buon design e perché?


Un'altra opzione (probabilmente una cattiva): vedi BitInteger[] java.math.BigInteger.divideAndRemainder(BitInteger val). Restituisce 2 numeri interi come valori di ritorno in un array.
EarlNameless,

Che tipo sono i due valori che vuoi restituire?
Tulains Córdova,

Risposte:


15

Direi questo secondo le seguenti linee:

  • Perché il tuo metodo restituisce esattamente più valori? Di che tipo di coesione stiamo parlando: quei valori dovrebbero effettivamente essere campi in una singola classe, o dovrebbero essere casualmente restituiti con lo stesso metodo, ma altrimenti non correlati? Se è quest'ultimo, potresti prendere in considerazione la possibilità di dividere il metodo in due metodi, piuttosto. Modifica: usa il tuo giudizio qui; a volte un tipo di coesione "casuale" può essere l'opzione migliore. Un'altra opzione è quella di utilizzare un costrutto coppia o tupla, sebbene in OOP, questi di solito non sono visti nelle API pubbliche (alcune eccezioni notevoli sono raccolte standard, ecc.).
  • Se i valori meritano di formare una classe, probabilmente consiglierei di non utilizzare una classe interna. Le classi interne sono in genere utilizzate come dettagli di implementazione interni, che sono nascosti dall'esterno. C'è qualche motivo per cui questo risultato non dovrebbe essere una classe "a pieno titolo", a sé stante?
  • Oltre a conservare i dati, quali operazioni sono applicabili a questa nuova classe? Nella progettazione orientata agli oggetti, vuoi avere il comportamento correlato vicino ai dati rilevanti (che sembrano essere anche le tue intenzioni). Il metodo a cui ti riferisci non dovrebbe piuttosto vivere in questa classe?

Riassumendo, vedrei se riesco a trasformare questo "oggetto dati" in una classe a tutti gli effetti con dati e comportamento. Come commento aggiuntivo, potresti voler rendere immutabile la classe, poiché il suo stato viene impostato una volta. Renderlo immutabile aiuterà a impedire che venga impostato in modo errato o modificato in un secondo momento (diciamo, qualcuno imposta uno dei campi su null e lo passa lungo).

Modifica: Come sottolinea correttamente Patkos Csaba, il principio che viene applicato qui è il principio di responsabilità singola ( SRP ) - la classe che stai cercando di creare dovrebbe davvero avere una responsabilità (definita come una ragione per cambiare ). Questa linea guida di progettazione dovrebbe aiutarti a capire se i tuoi due campi appartengono o meno a una singola classe. Per rimanere fedeli all'esempio di Wikipedia, la tua classe potrebbe essere vista come un tipo di rapporto, nel qual caso è conforme a SRP, ma è difficile commentare senza ulteriori informazioni.


Mentre sono d'accordo con l'idea generale di questa risposta, ci sono casi legittimi in cui due pezzi di dati strettamente correlati sono calcolati insieme, ma non ha senso legarli insieme in qualsiasi altra parte del programma. In tal caso potrebbe essere abbastanza pulito da restituire qualcosa del genere Pair<OneClass, AnotherClass>. Alcune persone non sarebbero d' accordo . In ogni caso, Pairdovrebbe essere un dettaglio di implementazione e non apparire mai in un metodo API pubblico.
9000,

@ 9000 Sono d'accordo; In realtà intendevo dire che la parola considera essere presa letteralmente in questo caso, cioè dividere il metodo potrebbe non essere sempre la soluzione migliore, è solo un'indicazione approssimativa in quella direzione. Farò una modifica in tal senso.
Daniel B,

1
Buona risposta. L'unica cosa che aggiungerei è un riferimento al Single Responsibility Principle (SRP) ( en.wikipedia.org/wiki/Single_responsibility_principle ). Se selezionato, è molto probabile che il metodo in discussione sia solo una semplice violazione di SRP e una divisione sia una soluzione semplice. Nella mia esperienza, ogni volta che un metodo vuole restituire 2 o più valori, nel 90% dei casi ci sono 2 metodi lì o un'altra classe dovrebbe essere estratta.
Patkos Csaba,

@PatkosCsaba grazie, ho fatto una modifica per includerlo. Di solito mi attengo a spiegare le cose in termini di accoppiamento e coesione, ma immagino che i principi SOLID siano visti come le regole di base per vivere ai nostri giorni.
Daniel B,

@DanielB: vedo SOLID come concetti di livello superiore e probabilmente più facili da comprendere. L'accoppiamento e la coesione sono ancora la linea di base, ma sono di livello più basso. SOLID fa un grande uso dell'accoppiamento e della coesione per spiegare i suoi principi e presentarli a un livello più generico.
Patkos Csaba,

10

C'è il concetto di una Tupla che è presente in altre lingue, come Python.

Si potrebbe restituire un'istanza di questa classe generica che è facilmente riutilizzabile:

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

2
A volte è bello avere un metodo statico generico create(), per evitare di dover specificare i parametri di tipo nel costruttore. Inoltre, chiamerei questa classe Pairpiuttosto che Tuple, dato che può rappresentare solo 2 tuple di valori.
agosto

5

Sembra che questa classe si stia allontanando da un'altra classe, e questo mi fa pensare che questo design non sia eccezionale.

Per un metodo che restituisce valori multible, preferirei

  • restituisce un contenitore generico (ad esempio un elenco o una mappa) contenente i valori di ritorno

o

  • crea una Classe per il valore di ritorno, che contiene solo i campi necessari + getter + un costruttore con tutti i campi

Esempio per la seconda opzione:

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

Rendere pubblici i campi aggiunge la possibilità di modificare i valori separatamente, anche se chiaramente i valori hanno una relazione (altrimenti non dovrebbero essere restituiti con lo stesso metodo). Scoraggerei fortemente questo schema in questa situazione speciale. Ma il punto principale è che la discussione sugli attributi pubblici e privati ​​non ha nulla a che fare con la domanda dei PO e non vedo alcun motivo per l'ultima frase in una risposta altrimenti buona.
scarfridge,

Il primo dovrebbe essere preferito.
Juanin,

scarfridge: punto preso, ultima frase rimossa.
user281377

2
Usa solo i campi finali pubblici, non c'è motivo di perdere tempo con gli accessori.
agosto


0

Risposta breve: è possibile restituire un array o un elenco con i due valori.

Personalmente scriverei due metodi distinti, come

int x = obj.getX();
int y = obj.getY();
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.