L'ereditarietà della classe (in C #) viola il Principio Aperto / Chiuso?

2

Generalmente faccio tutti i miei metodi private a meno che e fino a quando non debbano essere sostituiti da una sottoclasse. A quel punto modifico la classe base per rendere quel particolare metodo protected virtual . È corretto affermare che si tratta di una violazione del principio "Chiuso per modifica"? In tal caso, ciò implicherebbe che dovrei scegliere protected virtual come predefinito (simile a Java, in cui i metodi sono virtuali per impostazione predefinita). È davvero quello che raccomanda l'OCP?

Personalmente, una tale interpretazione mi sembra un po 'sciocca, ma voglio sapere se mi manca qualcosa.

    
posta kmote 29.09.2015 - 22:57
fonte

3 risposte

6

In primo luogo, penso che ci sia una differenza fondamentale tra lo sviluppo iniziale (qui di una classe base) e le fasi successive dello sviluppo.

Se ti trovi nella categoria di sviluppo iniziale, avrai spesso bisogno di un refactoring per fare in modo che la classe base accolga la sua prima sottoclasse (a volte aggiungi un parametro, dividi un metodo, ecc ...), quindi riguardo un refactoring del bisogno, Non mi preoccuperei dell'OCP in una fase così precoce.

Inoltre, non possiamo e non dobbiamo cercare di prevedere tutti i possibili e potenziali usi futuri di qualcosa; fare questo è un anti-modello in sé.

Inoltre, c'è una differenza fondamentale tra classi interne e classi esposte esternamente. Nel caso di classi interne, potrebbero essere tutte versioni messe insieme (ad esempio nella stessa DLL), quindi non vi è alcun motivo per impedire il refactoring di ospitare nuove sottoclassi man mano che il design si evolve.

Tuttavia, per le API esposte esternamente che hanno la maturità di un certo numero di sottoclassi (o plug-in di terze parti) già implementate (che si può anche prevedere di aggiungere a diverse unità di versioning (es. diverse DLL), si dovrebbe prendere l'OCP sul serio: un design migliore può ospitare più sottocategorie di terze parti senza modifiche.

Tuttavia, per quanto riguarda le API esposte esternamente, è preferibile utilizzare le interfacce invece della sottoclassificazione. La sottoclasse limita seriamente le opzioni di un implementatore rispetto alle interfacce, complicando al tempo stesso le interazioni e amp; dipendenze tra produttori e consumatori. Utilizzando un'interfaccia, evitiamo semplicemente il problema se contrassegnare un metodo come virtuale o meno. Quando scegliamo le interfacce, i design diventano più disaccoppiati, il che è il più nello spirito dell'OCP.

    
risposta data 29.09.2015 - 23:55
fonte
3

I typically make all my methods private unless and until they need to be overriden by a sub-class. At that point I modify the base class to make that particular method protected virtual

Questa è raramente una buona tattica. Quando crei una classe inizialmente, dovresti rendere tutti i metodi non esposti private . Punto. Questi metodi non diventeranno protected più tardi - in genere sono non progettati correttamente per essere sovrascritti in seguito, perché quando hai creato il metodo, lo scopo previsto non era quello di essere un punto di estensione. Rendere tutti loro protected virtual di default non lo cambierà - solo il fatto che tecnicamente puoi sovrascrivere un metodo non significa che questo abbia senso. Le classi, che devono essere istanziate da sole, raramente sono buone candidate per diventare classi base di altre classi, e provare a cambiare i loro metodi privati in "protetti" non è solo una violazione dell'OCP, ma porta molto probabilmente a una cattiva o uso errato dell'eredità.

D'altra parte, se progetti una abstract classe A in modalità "top down" per la quale sai che diventerà una classe base per le classi B, C e D, perché A è un po ' tipo di generalizzazione di B, C e D, in genere saprai sin dall'inizio quali metodi in A devono essere protetti per essere sovrascritti da B, C e D, quindi questi metodi sono protetti o pubblici fin dall'inizio e sono progettati per essere sovrascritti.

È possibile creare un tale progetto anche "dal basso verso l'alto": si inizia con una classe B, quindi si crea una seconda classe C, si vedono i punti in comune in entrambi e si rifattano i metodi comuni in una classe base comune o interfaccia comune A (invece di provare a fare C una derivazione di B, nel tentativo errato di seguire l'OCP). Ora i metodi in A che devono essere sovrascritti in B e C sono tutti pubblici o protetti fin dall'inizio. B e C devono essere modificati, ovviamente, ma B e C non sono le classi che sono estese - quindi chiedere l'OCP è inutile qui. Tuttavia, idealmente A non deve essere modificato in seguito quando un'altra classe D eredita da A. Se questo è il caso, A conferma l'OCP.

Quindi la linea di fondo è: le classi che seguono l'OCP non sono create da classi esistenti semplicemente rendendo qualsiasi metodo "protetto" - i "punti di estensione" devono essere scelti deliberatamente e progettati per quello scopo.

    
risposta data 30.09.2015 - 13:50
fonte
0

L'ereditarietà è un meccanismo corretto per soddisfare l'OCP. Vorrei tuttavia sottolineare che l'ereditarietà di OCP + può rompere l'LSP. Questo perché l'ereditarietà non supporta in modo adeguato comportamenti / responsabilità ortogonali.

Tuttavia, la semplice vecchia composizione può anche soddisfare l'OCP pur mantenendo felice l'LSP;) Uno di questi esempi sarebbe il modello di strategia. Un altro sarebbe il polimorfismo parametrico (noto come generici).

    
risposta data 12.03.2018 - 08:00
fonte

Leggi altre domande sui tag