Sta usando i parametri "out" o "ref" nei metodi Java per restituire valori extra cattivi?

6

Mi è capitato di creare una classe mutabile come questa:

class Mutable<T> {
    private T value;
    public Mutable() { this.value = null; }
    public Mutable(T value) { this.value = value; }
    T get() { return this.value; }
    void set(T value) { this.value = value; }
}

E poi viene spesso usato in un metodo come questo:

boolean operation(String input, Mutable<Set<String>> dataOut) throws ... {
    boolean result;
    try {
         String data = doSomething(input);
         result = validate(data);
         if (result && dataOut != null) {
             List<String> values = Arrays.asList(data.split(", "));
             Collections.sort(values);
             dataOut.set(new LinkedHashSet<String>(values));
         }
    } catch(SpecificIgnorableException ex) {
         result = false;
         logger.debug(ex);
    }
    return result;
}

... che è solo un esempio, potrebbe essere un caso d'uso, dove si userebbero ref o out parametri in C #, o parametri di riferimento non-const in C ++, o puntatori ai parametri di output in C.

In primo luogo, lo stesso potrebbe essere fatto utilizzando un array (con un elemento) anziché il tipo personalizzato sopra. Ha senso avere questo tipo personalizzato che dichiara chiaramente mutable , invece di usare un array mutabilmente implicito?

In secondo luogo, questo modello è cattivo e l'odore del codice in Java? Limitiamoci ai casi in cui l'utilizzo del parametro out avrebbe senso in C #. Dovrebbe essere sostituita ogni istanza di questo tipo di codice Java? Con cosa?

    
posta hyde 28.03.2013 - 17:25
fonte

4 risposte

12

La vera domanda è "le funzioni con effetti collaterali sono cattive?"

Fornire un riferimento a una variabile mutabile esplicita ("out") non è diverso dal fornire un Map che si modifica, o fare riferimento a una variabile globale all'interno della funzione. In tutti e tre i casi, la funzione è autorizzata a modificare qualcosa in un modo difficile da ragionare. Si consideri, ad esempio, una funzione che modifica il parametro "out", quindi genera.

Il contro-argomento è che questo non è diverso da una chiamata al metodo che modifica lo stato privato di un oggetto e poi genera.

Personalmente, se sto scrivendo un metodo focalizzato su un "risultato", preferirei creare una nuova classe per tenerlo; le classi sono economiche in Java. Se sto scrivendo un metodo che modifica lo stato interno di un oggetto, generalmente non restituisco nulla dal metodo.

    
risposta data 28.03.2013 - 17:43
fonte
4

Prima di tutto out e ref non hanno nulla a che fare l'uno con l'altro. Un parametro out in C # è l'unico modo in cui la lingua ha di restituire più valori da una funzione, a meno di creare un nuovo tipo da utilizzare come valore di ritorno. Solo perché è nella lista dei parametri è solo una cosa di sintassi. Non c'è equivalente in Java.

Penso che quello che hai lì sia un odore di codice perché non è proprio un idioma Java. Nella tua situazione, definirei semplicemente una nuova classe da utilizzare come valore risultato.

    
risposta data 28.03.2013 - 17:49
fonte
1

Prima di tutto, non penso che i metodi get / set per Mutable<T> realizzino molto rispetto semplicemente usando un campo esposto. Se una classe avrà un comportamento, l'uso dei metodi get / set consentirà alla classe una maggiore flessibilità nel modo in cui implementa tale comportamento, ma la tua Mutable<T> non ha un comportamento .

In secondo luogo, mentre i generici sono buoni, non possono gestire la maggior parte delle primitive in modo efficiente in Java. Potrebbe quindi essere necessario un MutableInt , MutableDouble , ecc. Oltre al mutabile generico [l'uso del modulo generico per Boolean può essere ok, poiché ci sono solo due possibili valori Boolean ed entrambi sono memorizzati nella cache] .

In terzo luogo, chiamare un metodo che restituisce dati in questo modo è un po 'complicato perché richiede al chiamante di creare un nuovo oggetto per accettare i dati di ritorno.

Tenuto conto di questi vincoli, tuttavia, direi che ci sono comunque casi in cui dare al chiamante un oggetto che può mutare è comunque l'approccio giusto. Se un oggetto vuole fornire un mezzo per chiedere la sua posizione, avere un metodo che restituisce Point avrà un'ambiguità inerente se l'oggetto restituito è "attaccato" alla posizione dell'oggetto e anche se rimarrà tale. Al contrario, un metodo che accetta un Point dal chiamante e aggiorna le sue coordinate per corrispondere alla posizione dell'oggetto non implica tale ambiguità, poiché non ci sarebbe alcuna ragione plausibile per un punto che fosse indipendente dalla posizione dell'oggetto di non rimanere tale.

    
risposta data 21.12.2013 - 17:22
fonte
1

In generale, questo tipo di schema dovrebbe essere evitato perché la sua usabilità. Non è così ovvio come chiamarlo correttamente, confrontandolo con la semplice funzione del valore di ritorno singolo.

Tuttavia, ha le sue prestazioni in termini di merito per il valore restituito da tipi primitivi o un'istanza che esiste già, specialmente in Java dove non è possibile restituire struct e restituire una nuova istanza di un oggetto ogni volta potrebbe non essere un'opzione per ciclo continuo. Confrontiamolo con il metodo Dictionary<T,U> di .Net C # TryGetValue :

public bool TryGetValue(
    TKey key,
    out TValue value
)

È lo stesso schema del tuo ed è lì per il motivo delle prestazioni, perché senza questo metodo il codice deve essere

if (dictionary.ContainsKey(key)) {
    int value = dictionary[key];
}

Che è più lento perché la stessa ricerca si verificherà due volte (per non parlare del multithreading per ora). Tuttavia, l'uso di ContainsKey è più chiaro in questo caso e la maggior parte dei codificatori lo userà per una ricerca banale piuttosto che usando TryGetValue .

Quindi la linea di fondo è, dipende da come la useresti. Dal codice, il sovraccarico dell'iniziazione del valore di ritorno sembra superare di gran lunga il vantaggio prestazionale del modello. Tuttavia, potresti voler fornire questa funzione per lasciare la stanza in un'ottica di ottimizzazione successiva.

    
risposta data 22.12.2013 - 16:12
fonte

Leggi altre domande sui tag