Una ben nota lacuna delle gerarchie classiche di classe è che sono cattive quando si tratta di modellare il mondo reale. Ad esempio, cercando di rappresentare le specie di animali con le classi. Ci sono in realtà diversi problemi quando lo faccio, ma uno che non ho mai visto una soluzione è quando una sottoclasse "perde" un comportamento o una proprietà che è stata definita in una super-classe, come un pinguino che non è in grado di volare (lì sono probabilmente esempi migliori, ma questo è il primo che mi viene in mente).
Da un lato, non si desidera definire, per ogni proprietà e comportamento, un flag che specifica se è presente, e controllarlo ogni volta prima di accedere a quel comportamento o proprietà. Vorresti solo dire che gli uccelli possono volare, semplicemente e chiaramente, nella classe Bird. Ma poi sarebbe bello se si potessero definire "eccezioni" in seguito, senza dover usare alcuni hack orribili ovunque. Ciò accade spesso quando un sistema è produttivo da un po 'di tempo. All'improvviso trovi un'eccezione che non si adatta affatto al design originale e non desideri modificare gran parte del codice per adattarlo.
Quindi, ci sono linguaggi o schemi di progettazione in grado di gestire in modo pulito questo problema, senza richiedere modifiche importanti alla "super-classe" e a tutto il codice che la usa? Anche se una soluzione gestisce solo un caso specifico, diverse soluzioni potrebbero costituire una strategia completa.
Dopo aver riflettuto di più, mi rendo conto che mi sono dimenticato del Principio di sostituzione di Liskov. Ecco perché non puoi farlo. Supponendo di definire "tratti / interfacce" per tutti i principali "gruppi di caratteristiche", puoi implementare liberamente tratti in diversi rami della gerarchia, come la caratteristica Volante potrebbe essere implementata da Uccelli, e qualche tipo speciale di scoiattoli e pesci.
Quindi la mia domanda potrebbe ammontare a "Come potrei non implementare un tratto?" Se la tua super classe è una Serializable Java, devi esserne uno anche se non è possibile per te serializzare il tuo stato, ad esempio se conteneva un "Socket".
Un modo per farlo è quello di definire sempre tutti i tratti a coppie dall'inizio: Volare e NotFlying (che genererebbe UnsupportedOperationException, se non confrontato). Il non-tratto non definirebbe alcuna nuova interfaccia e potrebbe essere semplicemente controllato. Sembra una soluzione "economica", in particolare se utilizzata sin dall'inizio.