2010-10-07 18 views
7

Supponiamo che io ho il seguente codice artificiosa:astratti metodi e il principio aperto-chiuso

abstract class Root 
{ 
    public abstract void PrintHierarchy(); 
} 

class Level1 : Root 
{ 
    override public void PrintHierarchy() 
    { 
    Console.WriteLine("Level1 is a child of Root"); 
    } 
} 

class Level2 : Level1 
{ 
    override public void PrintHierarchy() 
    { 
    Console.WriteLine("Level2 is a child of Level1"); 
    base.PrintHierarchy(); 
    } 
} 

Se sto solo guardando la classe Level2, posso subito vedere che Level2.PrintHierarchy segue the open/closed principle perché fa qualcosa sul suo proprio e chiama il metodo base che sta scavalcando.

Tuttavia, se guardo solo alla classe Level1, sembra essere in violazione di OCP perché non chiama base.PrintHierarchy - infatti, in C#, il compilatore lo vieta con l'errore "Impossibile chiamare una base astratta membro".

L'unico modo per rendere Level1 sembra seguire OCP è cambiare Root.PrintHierarchy ad un metodo virtuale vuoto, ma poi non posso più contare sulla compilatore di applicare derivante classi per implementare PrintHierarchy.

Il vero problema che sto avendo mantenendo il codice qui è vedere dozzine di metodi override che non chiamano base.Whatever(). Se base.Whatever è astratto, allora va bene, ma in caso contrario, il metodo Whatever potrebbe essere candidato per essere inserito in un'interfaccia invece che in un metodo di override concreto, oppure la classe o il metodo devono essere rifattorizzati in qualche altro modo, ma in entrambi i casi, indica chiaramente un design scadente.

Memorizzare che Root.PrintHierarchy è astratto o inserire un commento all'interno di Level1.PrintHierarchy, sono disponibili altre opzioni per identificare rapidamente se una classe formata come Level1 sta violando l'OCP?


C'è stata molta discussione nei commenti, e anche alcune buone risposte. Stavo avendo problemi a capire esattamente cosa chiedere qui. Penso che quello che mi sta frustrando è che, as @Jon Hanna points out, a volte un metodo virtuale indica semplicemente "Devi implementarmi" mentre altre volte significa "devi estendere me - se non riesci a chiamare la versione di base, rompi il mio progetto!" Ma C# non offre alcun modo per indicare quale di quelli che intendi, a parte quell'astratto o un'interfaccia chiaramente è una situazione da "implementare". (A meno che non ci sia qualcosa in Contratti di codice, che è un po 'fuori portata qui, penso).

Ma se una lingua ha avuto ha un decoratore must-attua o deve estendere, probabilmente creerebbe enormi problemi per il test dell'unità se non fosse possibile disabilitarlo. Ci sono lingue del genere? Questo suona piuttosto come design-by-contract, quindi non sarei sorpreso se fosse ad Eiffel, per esempio.

Il risultato finale è probabilmente as @Jordão says ed è completamente contestuale; ma lascerò la discussione aperta per un po 'prima di accettare ancora le risposte.

+0

Stai cercando uno strumento di analisi statica? Se sì allora forse questo potrebbe aiutare http://stackoverflow.com/q/1627024/119477 –

+0

@Conrad: speravo di ottenere ciò usando solo i miei occhi e il compilatore :) Ma se qualcun altro incontra questa domanda e * è * In cerca di uno strumento di analisi statica, ti consiglio vivamente di dare uno [Nitriq] (http://www.nitriq.com/) a un colpo prima di immergerti in NDepend. Ha un'edizione gratuita molto capace e anche la versione completa è abbastanza economica. –

+0

Non penso che il metodo in 'Level1' violi (o sembri violare) l'OCP. Anche se 'Level2' non ha chiamato base, non penserei che lo stia violando. L'OCP è un principio, non un modello che dice che dovresti chiamare i membri di base sui membri sottoposti a override. –

risposta

5

Root definisce quanto segue: Gli oggetti radice dispongono di un metodo PrintHierarchy. Non definisce nient'altro sul metodo PrintHierarchy di quello.

Level1 ha un metodo PrintHierarchy. Non smette di avere un metodo PrintHierarchy quindi non ha in alcun modo violato il principio open/closed.

Ora, più precisamente: Rinominare "PrintHierarchy" come "Foo". Level2 segue o viola il principio di apertura/chiusura?

La risposta è che non ne abbiamo la minima idea, perché non sappiamo quale sia la semantica di "Foo". Quindi non sappiamo se base.Foo dovrebbe essere chiamato dopo il resto del corpo del metodo, prima del resto, nel mezzo di esso, o per niente.

Qualora 1.ToString() ritorno "System.ObjectSystem.ValueType1" o "1System.ValueTypeSystem.Object" per mantenere questa pretesa di aperto/chiuso, o qualora assegnare la chiamata a base.ToString() ad una variabile inutilizzata prima di tornare "1"?

Ovviamente nessuno di questi. Dovrebbe restituire una stringa significativa come può. I suoi tipi di base restituiscono una stringa significativa come possono, e l'estensione non beneficia di una chiamata alla sua base.

Il principio di apertura/chiusura significa che quando chiamo Foo() mi aspetto qualche Fooing accada, e mi aspetto un po 'Livello1 Fooing appropriato quando lo chiamo io il Livello 1 e Livello 2 alcuni Fooing appropriato quando lo chiamo io il Livello 2. Se Level2 Fooing dovesse coinvolgere un po 'di Level1, anche il Fooing dipende da cosa è Fooing.

base è uno strumento che ci aiuta nell'estensione delle classi, non è un requisito.

3

Il non può decidere se un sistema (o classe) segue l'OCP semplicemente osservandolo staticamente, senza informazioni contestuali. Solo

se sa quali sono le probabili modifiche a un disegno, si può giudicare se si segue l'OCP per quei tipi specifici di modifiche.

Non esiste un costrutto linguistico che possa aiutarti in questo.

Una buona euristica sarebbe quella di utilizzare un qualche tipo di metrica, come instability and abstractness (pdf), o le metriche di Robert Martin per la mancanza di coesione per preparare al meglio i sistemi di cambiamento, e di avere una migliore possibilità di seguire l'OCP e tutto il altri importanti principi dell'OOD.

1

L'OCP è incentrato su precondizioni e post-condizioni: "quando è possibile sostituire solo la sua precondizione con una più debole e la sua post-condizionale con una più forte". Non penso che la base di chiamata nei metodi sovrascritti lo violi.

Problemi correlati