La modifica di un parametro in entrata è un antipattern? [chiuso]

54

Sto programmando in Java, e faccio sempre diventare i convertitori in questo modo:

public OtherObject MyObject2OtherObject(MyObject mo){
    ... Do the conversion
    return otherObject;
}

Nel nuovo ambiente di lavoro il modello è:

public void MyObject2OtherObject(MyObject mo, OtherObject oo){
    ... Do the conversion
}

Per me è un po 'puzzolente, dato che mi sono abituato a non cambiare i parametri in ingresso. Questa modifica dei parametri in entrata è antipattern o è OK? Ha alcuni seri inconvenienti?

    
posta CsBalazsHungary 23.06.2014 - 13:53
fonte

8 risposte

65

Non è un antipattern, è una cattiva pratica.

La differenza tra un antipattern e una mera pratica cattiva è qui: definizione anti-pattern .

Il nuovo stile di lavoro che mostri è una cattiva pratica , rudimentale o pre-OOP volte, secondo il Codice pulito dello zio Bob.

Arguments are most naturally interpreted as inputs to a function.

Anything that forces you to check the function signature is equivalent to a double-take. It’s a cognitive break and should be avoided. In the days before object oriented programming it was sometimes necessary to have output arguments. However, much of the need for output arguments disappears in OO languages

    
risposta data 23.06.2014 - 17:55
fonte
14

Citazione del famoso libro di Robert C. Martin "Codice pulito":

Output arguments should be avoided

Functions should have a small numbers of arguments

Il secondo modello viola entrambe le regole, in particolare l'argomento "argomento di output". In questo senso è peggio del primo modello.

    
risposta data 23.06.2014 - 14:03
fonte
14

Il secondo idioma può essere più veloce, perché il chiamante può riutilizzare una variabile su un ciclo lungo, invece di ogni iterazione creando una nuova istanza.

In genere non lo userei, ma ad es. nella programmazione del gioco ha il suo posto. Per esempio guarda la Vector3f di JavaMonkey molte operazioni permettono di passare l'istanza che dovrebbe essere modificata e restituito come risultato.

    
risposta data 23.06.2014 - 16:17
fonte
10

Non penso che siano due pezzi di codice equivalenti. Nel primo caso, devi creare otherObject . È possibile modificare l'istanza esistente nel secondo. Entrambi hanno usi. L'odore del codice preferirebbe uno su un altro.

    
risposta data 23.06.2014 - 13:57
fonte
6

Dipende davvero dalla lingua.

In Java il secondo modulo potrebbe essere un anti-pattern, ma alcuni linguaggi trattano il parametro che passa in modo diverso. Ad esempio, in Ada e VHDL, invece di passare per valore o per riferimento, i parametri possono avere le modalità in , out o in out .

È un errore modificare un parametro in o leggere un parametro out , ma eventuali modifiche a un parametro in out vengono restituite al chiamante.

Quindi i due moduli in Ada (anche questi sono VHDL legali) sono

function MyObject2OtherObject(mo : in MyObject) return OtherObject is
begin
    ... Do the conversion
    return otherObject;
end MyObject2OtherObject;

e

procedure MyObject2OtherObject(mo : in MyObject; oo : out OtherObject) is
begin
    ... Do the conversion
    oo := ... the conversion result;
end MyObject2OtherObject;

Entrambi hanno i loro usi; una procedura può restituire più valori in più parametri Out mentre una funzione può solo restituire un singolo risultato. Poiché lo scopo del secondo parametro è chiaramente indicato nella dichiarazione della procedura, non ci possono essere obiezioni a questo modulo. Io tendo a preferire la funzione per la leggibilità. ma ci saranno casi in cui la procedura è migliore, ad es. dove il chiamante ha già creato l'oggetto.

    
risposta data 24.06.2014 - 00:11
fonte
3

Sembra davvero maleodorante, e senza vedere più contesto è impossibile dirlo con certezza. Ci potrebbero essere due ragioni per farlo, sebbene ci siano alternative per entrambi.

In primo luogo, è un modo conciso per implementare la conversione parziale o lasciare il risultato con i valori predefiniti se la conversione fallisce. Cioè, potresti avere questo:

public void ConvertFoo(Foo from, Foo to) {
    if (can't convert) {
        return;
    }
    ...
}

Foo a;
Foo b = DefaultFoo();
ConvertFoo(a, b);
// If conversion fails, b is unchanged

Naturalmente, di solito questo viene gestito utilizzando le eccezioni. Tuttavia, anche se le eccezioni devono essere evitate per qualsiasi motivo, c'è un modo migliore per farlo: pattern TryParse come opzione.

Un altro motivo è che potrebbe essere per motivi di coerenza, ad esempio fa parte di un'API pubblica in cui questo metodo viene utilizzato per tutte le funzioni di conversione per qualsiasi motivo (ad esempio altre funzioni di conversione con più output).

Java non è eccezionale nel trattare con più uscite - non può avere parametri di solo output come alcune lingue, o avere più valori di ritorno come gli altri - ma anche ancora, puoi usare gli oggetti di ritorno.

La ragione della coerenza è piuttosto zoppa, ma purtroppo potrebbe essere la più comune.

  • Forse i poliziotti di stile sul posto di lavoro (o la tua base di codice) provengono da uno sfondo non Java e sono stati riluttanti a cambiare.
  • Il tuo codice potrebbe essere stato una porta da una lingua in cui questo stile è più idiomatico.
  • Potrebbe essere necessario che la tua organizzazione mantenga la coerenza dell'API in diverse lingue, e questo era lo stile minimo comune (è sciocco ma succede anche per Google ).
  • O forse lo stile ha avuto più senso nel passato remoto e si è trasformato nella sua forma attuale (ad esempio, avrebbe potuto essere il pattern TryParse ma un predecessore ben intenzionato ha rimosso il valore restituito dopo aver scoperto che nessuno lo ha affatto controllato) .
risposta data 24.06.2014 - 05:45
fonte
2

Il vantaggio del secondo modello è che costringe il chiamante ad assumere la proprietà e la responsabilità per l'oggetto creato. Non c'è dubbio che il metodo abbia creato o meno l'oggetto o ottenuto da un pool riutilizzabile. Il chiamante sa di essere responsabile della durata e dello smaltimento del nuovo oggetto.

Gli svantaggi di questo metodo sono:

  1. Non può essere utilizzato come metodo factory, il chiamante deve conoscere il sottotipo esatto di OtherObject di cui ha bisogno e costruirlo in anticipo.
  2. Non può essere utilizzato con oggetti che richiedono parametri nel costruttore se questi parametri provengono da MyObject . OtherObject deve essere costruibile senza conoscere MyObject .

La risposta è basata sulla mia esperienza in c #, spero che la logica si traduca in Java.

    
risposta data 23.06.2014 - 14:35
fonte
2

Data la semantica libera - myObjectToOtherObject potrebbe ugualmente significare che si spostano alcuni dati dal primo oggetto al secondo o che lo si converte completamente nuovo, il secondo metodo sembra più adeguato.

Tuttavia, se il nome del metodo fosse Convert (che dovrebbe essere se guardiamo la parte "... fa la conversione"), direi che il secondo metodo non avrebbe alcun senso. Non converti un valore in un altro valore già esistente, IMO.

    
risposta data 23.06.2014 - 16:52
fonte

Leggi altre domande sui tag