Technology HowTo

Programmazione funzionale vs. programmazione object-oriented: le basi per scegliere bene

Scegliere tra programmazione funzionale e programmazione object-oriented non è semplice, ma ci sono molti elementi chiave che possono aiutare a rendere la decisione più facile.

Pubblicato il 08 Mar 2022

codice

Adottare un particolare paradigma di programmazione è un passo importante in qualsiasi processo di sviluppo applicativo e se quando si tratta di modelli generali, ci sono tante opzioni tra cui scegliere, in questo caso specifico il campo di restringe e la maggior parte degli sviluppatori si trova sempre più spesso di fronte al dilemma tra la programmazione funzionale e la programmazione object-oriented. Quest’ultimo è un approccio di sviluppo ampiamente accettato, e spesso alla base dei programmi strutturati che la maggior parte degli sviluppatori impara a scrivere nelle prime fasi della carriera. Molti di questi linguaggi includono elementi che sono quasi indistinguibili dalle funzioni, ma sono molto lontani dai meccanismi che sono invece alla base di un linguaggio di programmazione puramente funzionale come Haskell.

Passando in rassegna le principali differenze tra la programmazione funzionale e quella object-oriented anche attraverso alcuni esempi di come funzionano, sarà possibile individuare i fattori chiave nella scelta tra questi paradigmi di codifica.

Metodo di programmazione funzionale vs. object-oriented

La programmazione funzionale si comporta come le comuni funzioni matematiche, come i calcoli dietro una conversione da Celsius a Fahrenheit. Con le funzioni, gli stessi input porta costantemente allo stesso risultato. Una funzione “pura” è deterministica e non produce “effetti collaterali” – in altre parole, restituisce sempre lo stesso valore quando viene chiamata e non modifica nulla al di fuori del suo ambito o dei suoi parametri.

Un esempio incredibilmente potente del metodo funzionale è l’implementazione di Google di MapReduce e il suo approccio per restituire risultati a certi input di ricerca. MapReduce li collega sotto forma di coppie chiave/valore usando una funzione chiamata reduce tramite un processo che aggrega i termini e assegna a ciascuno di essi un valore che indica quale tipo di risultato dovrebbe restituire. Dato lo stesso set di dati, Google sputerà fuori la stessa risposta ogni volta senza sorprese.

D’altra parte, la programmazione object-oriented può contenere variabili dipendenti dallo stato, il che significa che gli oggetti non mantengono necessariamente valori coerenti. Per esempio, se chiamate un modulo che restituisce uno stipendio, potreste ottenere 50.000 dollari. Ma se se ne esegue un altro che aggiunge il 10% a quel totale, poi si chiede di nuovo lo stipendio, il valore restituito è 55.000 dollari. I programmi object-oriented possono anche contenere elementi come variabili globali e variabili statiche che fanno variare ogni volta le risposte alle stesse richieste.

Per aggiungere stati ai programmi funzionali, si usa un linguaggio di programmazione non puramente funzionale, come F#, è possibile anche aggiungere funzioni ad un programma più tradizionale come LINQ.

Esempi di codice per la programmazione funzionale vs. programmazione object-oriented

Il seguente è un esempio di codice per la programmazione funzionale con FizzBuzz in F#. FizzBuzz è un comune test di codifica in cui gli sviluppatori creano un programma che stampa una serie di lettere e numeri basati su un semplice insieme di regole. Per prima cosa, se il numero è divisibile per tre, stampa la parola Fizz al suo posto. Poi, per i numeri divisibili per cinque, si sostituisce la parola Buzz. Infine, se il numero è divisibile sia per tre che per cinque, si stampa FizzBuzz. In un linguaggio funzionale, come F#, questa logica può essere strutturata come funzioni. Il programma è composto interamente da queste funzioni, come mostrato nell’esempio seguente.

Segue poi l’approccio object-oriented usando C# come linguaggio. Si può notare che la logica è simile, la grande differenza è che, nel secondo codice, tutto è contenuto in un oggetto che mantiene il numero corrente del ciclo come variabile. Nell’approccio object-oriented ci sono alcuni vantaggi. Prima di tutto se l’applicazione è costruita con una serie di logiche, gli oggetti possono interagire attraverso interfacce semplificate, inoltre per fare una serie di “giochi” simili a FizzBuzz, un programmatore potrebbe usare l‘ereditarietà per aggiungere e cambiare la logica come necessario. Ecco l’implementazione C# così come classe così come una routine principale più tradizionale per eseguire il processo di stampa.

Passando ad un livello più alto di astrazione, un oggetto potrebbe andare in loop dall’inizio alla fine e chiamare il ciclo e sarebbe utile se il programmatore vuole scambiare le logiche di runtime, o costruire il programma usando regole tratte da un file che potrebbe cambiare nel tempo. Segue l’esempio di una vera implementazione object-oriented C# di FizzBuzz realizzata da Steve Poling, un ex ingegnere del software e consulente tecnico di Excelon Development in cui ci sono più linee di codice che possono servire per il riutilizzo.

Casi d’uso di programmazione funzionale vs. programmazione object-oriented

La progettazione dell’interfaccia utente è una procedura naturale per l’approccio object-oriented. Le finestre che appaiono sullo schermo di un utente sono spesso costruite usando pulsanti, caselle di testo e menu e hanno uno stato. Con il testo su una pagina in un elaboratore di testi, ad esempio, tale stato cambia mentre l’utente digita. Una volta che il layout di base di una finestra è disponibile, altri programmatori possono accedervi e riutilizzare quel codice, iniziando con una shell e riempiendo gli oggetti.

La programmazione funzionale è l’opzione migliore quando l’applicazione è composta da funzioni che si costruiscono l’una sull’altra. Per esempio, in Unix è comune legare insieme un gran numero di piccoli programmi, inviando i risultati di un elenco di processi ad una ricerca specifica come grep, o ad una pagina alla volta con less.

Combinare la programmazione funzionale e object-oriented

Un grande svantaggio della programmazione object-oriented è il rischio di creare un codice base complesso e sempre più difficile da gestire nel tempo. Anche per sofisticati esperti di sviluppo che usano l’approccio object-oriented il codice spesso assomiglia all’esempio precedente con pochi oggetti sparsi tra una discreta quantità di codice procedurale.

Può essere difficile testare o fare il debug di oggetti che creano altri oggetti o hanno legami con database esterni e API.

Nel caso di errore, è improbabile che il programmatore conosca i valori in tutti gli oggetti o esattamente come replicarli, anche con una registrazione completa dell’errore. Ci sono certamente alcuni design pattern che affrontano questi problemi, ma hanno un livello di adozione ancora piuttosto limitata. In ogni caso, quando lo sviluppo è object-oriented diventano necessari spesso difficili revisioni del codice e interventi di manutenzione.

La programmazione funzionale, al contempo, presenta un’altra criticità: può risultare molto difficile da imparare e mettere in pratica. L’approccio funzionale richiede di pensare al codice in modo completamente diverso e ciò comporta un considerevole investimento di tempo e una rigorosa attenzione ai dettagli. Per queste ragioni, la leadership IT può vedere la programmazione funzionale come un rischio.

Esiste però la possibilità di un compromesso: i programmatori possono usare l’approccio funzionale per creare una piccola parte di una grande applicazione. Qualsiasi logica che prende dati e produce risultati in batch è un buon candidato per questa strategia che può riguardare ad esempio le routine di quotazione assicurativa, la programmazione dei prodotti e l’estrazione, trasformazione e caricamento.

È poi possibile aggiungere stati ai programmi funzionali usando un linguaggio di programmazione che non è puramente funzionale, come F#. Allo stesso modo è possibile aggiungere funzioni a programmi basati su linguaggi tradizionali, object-oriented. Per esempio, linguaggi come C# e Java ora possiedono alcune caratteristiche che permettono un approccio funzionale, inclusa la capacità di scrivere codice in F# che interagisce con C# attraverso il Common Language Runtime.

Valuta la qualità di questo articolo

La tua opinione è importante per noi!

Articoli correlati