C'è una buona ragione per rendere non pubbliche le funzioni pure?


25

Ho avuto un piccolo dibattito in corso con un collega. In poche parole, c'è una buona ragione per nascondere / incapsulare funzioni che sono pure?

Per "puro" intendo la definizione di Wikipedia :

  • Restituisce sempre gli stessi risultati dallo stesso input. (Per motivi di questa discussione Foo Create(){ return new Foo(); }è considerato impuro se Foonon ha una semantica di valore.)
  • Non utilizza lo stato modificabile (tranne le variabili locali) o I / O.
  • Non produce effetti collaterali.

26
Potrebbe esserci un argomento per non rendere affatto tale funzione membro di una classe.
Pieter B,

6
@PieterB - in effetti, sebbene non tutte le lingue lo supportino, e alcune lingue consentono il modulo interno anche per funzioni gratuite.
Telastyn,

3
Qual è stata la tua opinione? Non penso che la purezza della funzione sia correlata al fatto che appartenga o meno all'API pubblica.
Andres F.,

@andresF. - il dibattito riguardava in realtà l'opportunità di rendere pubblica una regola di convalida senza restrizioni. Ho sostenuto che, poiché si trattava di una funzione pura, c'erano pochi danni. Per questo particolare esempio, ha aiutato la testabilità e probabilmente sarebbe stato riutilizzato. Questa domanda riguarda più ampiamente quanto tale argomento potrebbe / dovrebbe essere applicato.
Telastyn,

2
@Telastyn Se è riutilizzabile ma non fa parte della responsabilità della classe corrente, dovrebbe probabilmente essere in una classe / modulo separato / qualunque sia il tuo linguaggio. Quindi, essendo parte della responsabilità della nuova classe / modulo, sarebbe necessariamente reso pubblico. Dato che menzioni i test, noterò che non devi necessariamente deridere il metodo quando lo fai. Come "dettaglio di implementazione", deriderlo durante i test offre pochi vantaggi.
jpmc26,

Risposte:


76

Una funzione pura potrebbe essere ancora un dettaglio di implementazione. Sebbene la funzione non possa causare alcun danno (dal punto di vista della non rottura di invarianti / contratti importanti), esponendola perdono sia l'autore che gli utenti di quella classe / modulo / pacchetto. L'autore perde perché ora non può rimuoverlo anche se l'implementazione cambia e la funzione non gli è più utile. Gli utenti perdono perché devono esaminare e ignorare le funzioni extra che non sono rilevanti per l'utilizzo dell'API per capirlo.


33
+1. I membri pubblici della tua classe sono le sue API. Non si tratta di "Può farmi del male se è pubblico?", Ma piuttosto "Dovrebbe essere parte dell'API?"
Ordous,

1
L'unica cosa che aggiungerei è che se inizi a vedere funzioni simili spuntare in altre posizioni, trasformale in un'altra classe in modo che più classi possano farne uso. O se questa è una preoccupazione per cominciare, definirla in una classe separata per cominciare.
jpmc26,

1
Sigillerei anche le classi pubbliche che non sono pensate per essere estese.
Den

+1 per indicare che nascondere o mostrare una funzione (o classe) è principalmente una questione di protezione e manutenzione di un'API e non correlata al tipo di codice che è.
Marco,

42

La domanda è al contrario.

Non cerchi un motivo per rendere una funzione non pubblica. È una mentalità errata per cominciare (secondo me). Il ragionamento dovrebbe andare dall'altra parte.

In altre parole, non chiedere "perché dovrei renderlo privato?". Chiedi: "perché dovrei renderlo pubblico?"

In caso di dubbio, non esporlo. È un po 'come il rasoio di Ockham: non moltiplicare le entità oltre la necessità.

EDIT: affrontare i controargomenti sollevati da @Telastyn nei commenti (per evitare discussioni approfondite lì):

L'ho sentito nel tempo, e l'ho persino sposato per un bel po 'di tempo, ma nella mia esperienza, le cose tendono ad essere troppo private.

Sì, a volte è una seccatura se una classe è aperta per eredità, ma non è possibile ignorare alcuni metodi privati ​​(il cui comportamento si desidera modificare).

Ma protectedbasterebbe - ed è ancora non pubblico.

Porta a un sacco di duplicazione del codice e sovraccarico per arrivare a "cose ​​che non dovrebbero essere pubbliche", ma sono comunque accessibili indirettamente.

Se diventa problematico, allora semplicemente renderlo pubblico! C'è la necessità di cui stavo parlando :)

Il mio punto è che non dovresti farlo per ogni evenienza (YAGNI e tutti).

Nota che è sempre più facile rendere pubblica una funzione privata piuttosto che riportarla alla privacy. È probabile che quest'ultimo rompa il codice esistente.


L'ho sentito nel tempo, e l'ho persino sposato per un bel po 'di tempo, ma nella mia esperienza, le cose tendono ad essere troppo private. Porta a un sacco di duplicazione del codice e sovraccarico per arrivare a "cose ​​che non dovrebbero essere pubbliche", ma sono comunque accessibili indirettamente.
Telastyn,

3
@Telastyn IMHO, sembra che l'applicazione soffra di alcuni passi falsi di progettazione; se ti trovi incline a esporre un metodo perché devi invocarlo attraverso percorsi di codice discreti, è probabilmente un segno che questo metodo dovrebbe essere suddiviso nel suo modulo che può essere iniettato o incluso dove appropriato, specialmente se è una pura funzione .
leo,

@leo - scusa, non lo seguo. Considera una funzione come intero inferiore a. Questa è una bella funzione pura che viene esposta specificamente perché può quindi essere iniettata e inclusa (o semplicemente usata) dove appropriato.
Telastyn,

5
@Telastyn Secondo me questo è un sintomo della filosofia "tutto è un oggetto / classe" unita al fatto che alcuni linguaggi OOP non hanno tipi di dati compositi diversi dalle classi. Non appena qualcuno deve passare due valori contemporaneamente, finisce per scrivere una classe, quindi ogni collega e software di controllo dello stile ti dirà di rendere quei campi privati.
Doval,

@Telastyn Right. Quello che voglio dire è che se il tuo metodo 'integerLessThan' è iniziato come un dettaglio di implementazione incapsulato di un metodo pubblico, ma scopri che devi invocare il metodo privato in altri luoghi, è probabilmente un segno che il metodo privato appartiene a un altro modulo / pacchetto / classe in modo che possa essere importato indipendentemente dalla logica che lo invoca. Pubblicizzare semplicemente il metodo così com'è significherebbe che il metodo si trova arbitrariamente nel primo modulo dove lo hai trovato utile.
leo,

6

Non penso che la decisione di nascondere / incapsulare una funzione debba dipendere dalla sua purezza. Solo perché una funzione è pura non significa che gli esterni debbano conoscerla. È interessante notare che, se la funzione è pura e pensata per essere pubblica, forse non ha nemmeno bisogno di essere un membro di istanza dell'interfaccia, forse è più adatta come statica. Ma tutto ciò dipende dall'intento del contratto e in questo caso dal raggruppamento logico di funzionalità, non dalla purezza della funzione.


5

Le lezioni dovrebbero aderire al principio di responsabilità unica . Sebbene una classe possa dover ricorrere ad altre funzionalità per raggiungere i suoi obiettivi, dovrebbe solo esporre funzioni che fanno parte della sua unica responsabilità.

Ecco solo un esempio di un caso in cui la visibilità potrebbe causare un problema.

Prendi in considerazione una classe che modifica i widget. Forse come parte del suo codice frobnication ha bisogno di alcune funzioni di utilità che analizzano una stringa: forse deve trasformare il nome del widget in un modo che le funzioni di stringa standard non supportano.

Poiché questa è una funzione pura (la stringa entra, la trasforma in qualche modo, restituisce una nuova stringa), potrebbe essere pubblica o privata senza conseguenze. O potrebbe?

Se lo rendi pubblico, ora la tua classe ha due responsabilità: frobnicizzare i widget e trasformare le stringhe. Ciò viola SRP e può causare problemi se altre classi fanno affidamento sulla funzione. Dal momento che questo è qualcosa che pensi sia usato solo all'interno della classe, forse ne cambi l'interfaccia o la visibilità. Ora le classi in altre parti del sistema sono interrotte.

Mantenendo la funzione privata, nessuno ha mai l'opportunità di fare affidamento su codice che non fa parte della singola responsabilità della classe.


2
Direi che dovrebbe contenere solo funzioni che fanno parte della sua unica responsabilità, non solo esporre.
Telastyn,

1
Penso che ci sia una zona grigia. Hai davvero bisogno di una StringTransformerclasse separata per incapsulare due o tre righe di codice che vengono utilizzate solo in un posto? Sono d'accordo che una volta che il codice viene utilizzato in più punti, è meglio separarlo in una nuova classe con una responsabilità, ma c'è un compromesso.

Certamente. Le linee guida sono linee guida, non regole.
Telastyn,

@snowman in non java ti basta creare una libreria di funzioni.
Pieter B,

@PieterB non si tratta di Java v. Nient'altro. Per renderlo veramente OO avresti bisogno di una fabbrica, alcune classi astratte, ecc.
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.