Più apprendo su diversi paradigmi di programmazione, come la programmazione funzionale, più inizio a mettere in discussione la saggezza dei concetti OOP come l'ereditarietà e il polimorfismo. Ho imparato per la prima volta l'ereditarietà e il polimorfismo a scuola, e al momento il polimorfismo sembrava un modo meraviglioso per scrivere codice generico che consentisse una facile estensibilità.
Ma di fronte alla digitazione anatra (sia dinamica che statica) e alle funzioni funzionali come le funzioni di ordine superiore, ho iniziato a considerare l'ereditarietà e il polimorfismo come l'imposizione di una restrizione non necessaria basata su un fragile insieme di relazioni tra oggetti . L'idea generale dietro al polimorfismo è che scrivi una funzione una volta, e dopo puoi aggiungere nuove funzionalità al tuo programma senza modificare la funzione originale: tutto ciò che devi fare è creare un'altra classe derivata che implementa i metodi necessari.
Ma questo è molto più semplice da ottenere attraverso la digitazione anatra, sia che si tratti di un linguaggio dinamico come Python, sia di un linguaggio statico come il C ++.
Ad esempio, considera la seguente funzione Python, seguita dal suo equivalente C ++ statico:
def foo(obj):
obj.doSomething()
template <class Obj>
void foo(Obj& obj)
{
obj.doSomething();
}
L'equivalente OOP sarebbe simile al seguente codice Java:
public void foo(DoSomethingable obj)
{
obj.doSomething();
}
La principale differenza, ovviamente, è che la versione Java richiede la creazione di un'interfaccia o di una gerarchia di ereditarietà prima che funzioni. La versione Java comporta quindi più lavoro ed è meno flessibile. Inoltre, trovo che la maggior parte delle gerarchie di ereditarietà del mondo reale siano alquanto instabili. Abbiamo visto tutti gli esempi inventati di Shapes and Animals, ma nel mondo reale, quando cambiano le esigenze del business e vengono aggiunte nuove funzionalità, è difficile eseguire qualsiasi lavoro prima che sia necessario allungare la relazione "is-a" tra sottoclassi, oppure rimodellare / refactare la propria gerarchia per includere ulteriori classi base o interfacce al fine di soddisfare nuovi requisiti. Con la digitazione anatra, non devi preoccuparti di modellare nulla: ti preoccupi solo della funzionalità di cui hai bisogno.
Tuttavia, eredità e polimorfismo sono così popolari che dubito che sarebbe molto esagerato definirli la strategia dominante per l'estensibilità e il riutilizzo del codice. Allora perché ereditari e polimorfismi hanno così successo? Sto trascurando alcuni seri vantaggi che l'ereditarietà e il polimorfismo hanno una digitazione eccessiva?