Ciao, e benvenuto nella settima puntata della ventitreesima stagione. Nelle scorse puntate abbiamo esplorato la concorrenza, il superpotere di Go. Oggi torniamo alla struttura del codice, e vediamo come Go affronta un problema classico: come organizzare il codice, come fare in modo che pezzi diversi lavorino insieme in modo flessibile. E qui Go fa una scelta interessante, che riprende e rovescia qualcosa che abbiamo visto nella stagione sui linguaggi. Rinuncia a un meccanismo classico e ne adotta uno diverso, più semplice ed elegante: le interfacce. Oggi capiamo cosa sono e perché rappresentano una filosofia diversa di organizzare il codice.
Partiamo da ciò che Go deliberatamente non ha, perché è significativo. Ricordi, dalla stagione sui linguaggi, il modo di pensare a oggetti, con l'idea di ereditare, di costruire nuove cose a partire da altre estendendole, in gerarchie di tipi che discendono l'uno dall'altro? Ebbene, Go, coerente con la sua filosofia di semplicità, ha scelto di non avere questo meccanismo dell'ereditarietà classica. Ha ritenuto che, per quanto potente, l'ereditarietà porti spesso a gerarchie complicate, rigide e difficili da capire, e che ci sia un modo più semplice per ottenere flessibilità. Quindi Go ha rinunciato all'ereditarietà, un'altra funzionalità tolta in nome della semplicità, e ha puntato tutto su un'idea diversa.
L'idea diversa sono le interfacce, e voglio spiegartela con calma, perché è elegante. Un'interfaccia, in Go, descrive un comportamento, cioè un insieme di cose che qualcosa sa fare. Non descrive com'è fatto un oggetto, ma cosa sa fare. Per esempio, potresti avere un'interfaccia che dice sa essere stampato, o sa essere salvato: descrive una capacità, un comportamento, senza dire nulla su chi lo possiede o com'è fatto dentro. L'interfaccia è come un contratto che dice: chiunque sappia fare queste cose, va bene per me. Si concentra su cosa si sa fare, non su cosa si è. È un modo di ragionare per capacità, non per identità.
Ora arriva la parte più bella e caratteristica delle interfacce di Go, quella che voglio farti apprezzare: la loro soddisfazione è implicita. Cosa significa? Significa che qualsiasi cosa sappia fare ciò che un'interfaccia richiede, soddisfa automaticamente quell'interfaccia, senza doverlo dichiarare esplicitamente. Non devi dire questa cosa aderisce a quell'interfaccia: se sa fare ciò che l'interfaccia descrive, allora la soddisfa, punto. È un po' il principio del se cammina come un'anatra e starnazza come un'anatra, allora per i nostri scopi è un'anatra. Non conta come si chiama o da dove viene: conta che sappia fare le cose giuste. Questa adesione automatica, senza dichiarazioni, rende il sistema sorprendentemente flessibile e disaccoppiato.
Voglio farti capire perché questa flessibilità sia così preziosa, con un esempio concettuale. Immagina di scrivere una parte del tuo programma che ha bisogno di qualcosa che sappia essere salvato. Grazie alle interfacce, tu scrivi quella parte dicendo semplicemente: io lavoro con qualsiasi cosa sappia essere salvata. Non ti leghi a un tipo specifico di oggetto: ti leghi a un comportamento. Così, qualsiasi cosa, oggi o in futuro, sappia essere salvata, potrà funzionare con la tua parte di programma, anche cose che non esistevano quando l'hai scritta. Questo tiene le parti del programma poco legate tra loro, dipendenti solo dai comportamenti di cui hanno bisogno, non dai dettagli specifici. E parti poco legate sono più facili da cambiare, riusare, combinare in modi nuovi.
Voglio inquadrare tutto questo in una scelta filosofica più ampia, che riprende un tema del podcast: comporre invece di ereditare. L'ereditarietà costruisce gerarchie rigide, in cui le cose discendono l'una dall'altra in strutture fisse. La composizione, che è la via di Go, costruisce invece assemblando pezzi indipendenti che collaborano attraverso i loro comportamenti, come mattoncini che si combinano in modi flessibili. Ricordi la filosofia dei piccoli strumenti componibili di Linux, o il costruire cose complesse combinando pezzi semplici? Le interfacce di Go portano questo spirito nell'organizzazione del codice: invece di grandi gerarchie ereditate, tanti pezzi che si compongono liberamente in base a cosa sanno fare. È di nuovo la preferenza per la composizione flessibile sulla struttura rigida.
Voglio farti apprezzare come questa scelta sia perfettamente coerente con tutta la filosofia di Go, perché mostra un disegno unitario. Rinunciare all'ereditarietà e puntare sulle interfacce è, ancora una volta, togliere un meccanismo complicato in favore di uno più semplice ed elegante. Le interfacce sono minimali, si concentrano solo sui comportamenti, e la loro adesione implicita elimina ogni cerimonia. È lo stesso spirito del linguaggio piccolo, della semplicità radicale, applicato al modo di strutturare il codice: meno gerarchie rigide, più composizione flessibile; meno dichiarazioni, più comportamento; meno complicazione, più chiarezza. Ogni scelta di Go, vedi, discende dalla stessa filosofia di fondo, e le interfacce ne sono un esempio bellissimo.
Per oggi ci fermiamo qui. Per organizzare il codice, Go rinuncia all'ereditarietà classica, giudicata fonte di gerarchie rigide e complicate, e punta sulle interfacce. Un'interfaccia descrive un comportamento, cosa qualcosa sa fare, non com'è fatto, e la sua adesione è implicita: qualsiasi cosa sappia fare ciò che l'interfaccia richiede la soddisfa automaticamente, come l'anatra che cammina e starnazza. Questo tiene le parti del programma poco legate, dipendenti solo dai comportamenti, e quindi flessibili e componibili. È la scelta di comporre invece di ereditare, lo stesso spirito dei piccoli pezzi combinabili di Linux, coerente con tutta la semplicità di Go. Nella prossima puntata affrontiamo un'altra scelta caratteristica: gli errori come valori. Nelle note trovi qualche spunto. Se ti è utile, condividila. Grazie per l'ascolto, e ci sentiamo alla prossima.