which [would be] the best implementation...?
La risposta migliore è "dipende dallo spazio business / dominio e dal tuo design".
Il Consiglio speciale dell'Università potrebbe essere la sua stessa cosa; un intero contesto di dati + comportamento a sé. Questo è per l'analisi dei requisiti da decidere. Quindi si tratta di come una cosa viene utilizzata in tutta l'applicazione.
Una classe è data + comportamento. Una classe non è semplicemente "identità". Se stai progettando sottoclassi senza alcun comportamento sovrascritto o specializzato, dai un'occhiata al tuo design.
Qualsiasi cosa fatta nel mondo reale è un compromesso. In che modo la totalità del design di classe soddisfa al meglio i requisiti così come sono stati informati dai principi di progettazione OO?
Cerco i concetti e le entità fondamentali e creo classi semplici di questi. Da lì ho la flessibilità sia per l'ereditarietà che per la composizione. Posso progettare classi di livello superiore in modi diversi con le stesse parti costituenti. Applica questa idea a tutti i livelli di astrazione.
Se pensi di più sull'API, i messaggi, la comunicazione che desideri tra gli oggetti, i dettagli di implementazione tendono a sentirsi meno problematici.
Le classi astratte sono fantastiche fornendo logica e processi comuni ma lasciando segnaposti per comportamenti specializzati. Il modello di modello e i metodi del modello sono ciò di cui sto parlando.
Al contrario, la composizione può aiutare quando vogliamo fare la stessa cosa, ma in modi sostanzialmente diversi. Ad esempio .net ha "adattatori dati" per vari database. Una classe "wrapper" fornisce un'API coerente mentre le classi contenute comunicano con il DB stesso. La parola chiave per l'interfaccia coerente è una facciata. Infatti questi adattatori di dati sono sia "adattatori" che "facciate".
"Interfacce" (come nella parola chiave interface
in C # e Java) è un buon modo per dichiarare un comportamento specializzato su classi non correlate.