In realtà, il codice OO è molto meno riutilizzabile, e questo è in base alla progettazione. L'idea alla base di OOP è di limitare le operazioni su particolari parti di dati a determinati codici privilegiati che si trovano nella classe o nella posizione appropriata nella gerarchia dell'ereditarietà. Questo limita gli effetti avversi della mutabilità. Se una struttura dati cambia, ci sono solo tanti punti nel codice che possono essere responsabili.
Con l'immutabilità, non ti importa chi può operare su una data struttura dati, perché nessuno può cambiare la tua copia dei dati. Ciò rende molto più semplice la creazione di nuove funzioni per lavorare su strutture dati esistenti. Basta creare le funzioni e raggrupparle in moduli che sembrano appropriati dal punto di vista del dominio. Non devi preoccuparti di dove collocarli nella gerarchia dell'ereditarietà.
L'altro tipo di riutilizzo del codice sta creando nuove strutture dati per lavorare su funzioni esistenti. Questo viene gestito in linguaggi funzionali utilizzando funzionalità come generici e classi di tipi. Ad esempio, la classe di tipi Ord di Haskell ti consente di usa la funzione sort
su qualsiasi tipo con un'istanza Ord
. Le istanze sono facili da creare se non esistono già.
Prendi il tuo esempio di Animal
e valuta l'implementazione di una funzione di alimentazione. L'implementazione OOP semplice consiste nel mantenere una collezione di oggetti Animal
e nel loro scorrere tutti, chiamando il metodo feed
su ciascuno di essi.
Tuttavia, le cose si complicano quando si arriva ai dettagli. Un oggetto Animal
sa naturalmente che tipo di cibo mangia e quanto serve per sentirsi pieno. non naturalmente sa dove viene conservato il cibo e quanto è disponibile, quindi un oggetto FoodStore
è appena diventato una dipendenza di ogni Animal
, sia come un campo dell'oggetto Animal
o passato come parametro del metodo feed
. In alternativa, per mantenere la classe Animal
più coesa, potresti spostare feed(animal)
sull'oggetto FoodStore
, oppure potresti creare un abominio di una classe chiamata AnimalFeeder
o alcuni di questi.
In FP, non vi è alcuna inclinazione per i campi di una Animal
a rimanere sempre raggruppati, il che ha alcune implicazioni interessanti per la riusabilità. Supponiamo che tu abbia un elenco di Animal
record, con campi come name
, species
, location
, food type
, food amount
, ecc. Hai anche un elenco di FoodStore
record con campi come location
, food type
e food amount
.
Il primo passo nell'alimentazione potrebbe essere quello di mappare ciascuno di questi elenchi di record a liste di coppie (food amount, food type)
, con numeri negativi per gli importi degli animali. È quindi possibile creare funzioni per fare ogni sorta di cose con queste coppie, come sommare le quantità di ogni tipo di cibo. Queste funzioni non appartengono perfettamente a un modulo Animal
o a FoodStore
, ma sono altamente riusabili da entrambi.
Finisci con un sacco di funzioni che fanno cose utili con [(Num A, Eq B)]
che sono riutilizzabili e modulari, ma hai problemi a capire dove metterle o come chiamarle come gruppo. L'effetto è che i moduli FP sono più difficili da classificare, ma la classificazione è molto meno importante.