2010-06-30 3 views
31

Ogni volta che sovrascrivo un metodo di una classe base, diversa dalla mia implementazione di questo metodo, mi sembra di avere 3 scelte.Come "correttamente" sovrascrivere un metodo di classe base?

1) Chiamare base.Method() e quindi fornire la mia implementazione.

2) Fornire mia implementazione e quindi chiamare base.Method()

3) solo fornire la mia esecuzione.

Recentemente durante l'utilizzo di una libreria ho realizzato alcuni bug introdotti a causa dell'impossibilità di implementare il metodo come previsto dalla libreria. Non sono sicuro che ciò non funzioni su una parte della biblioteca o qualcosa di sbagliato nella mia comprensione.

Farò un esempio.

public class ViewManager { 
    public virtual void Customize(){ 
     PrepareBaseView(); 
    } 
} 

public class PostViewManager { 
    public override void Customize(){ 
     base.Customize(); 
     PreparePostView(); 
    } 
} 


public class PreViewManager { 
    public override void Customize(){ 
     PreparePreView(); 
     base.Customize(); 
    } 
} 


public class CustomViewManager { 
    public override void Customize(){ 
     PrepareCustomView(); 
    } 
} 

La mia domanda è che come potrebbe un sapere classe figlia (senza dare un'occhiata alla implementazione della classe base) quale ordine (o di opzione) che viene previsto per la classe padre? Esiste un modo in cui la classe genitore potrebbe imporre una delle tre alternative a tutte le classi derivate?

+1

'PostViewManager',' PreViewManager', 'CustomViewManager' ereditano da' ViewManager'? Se è così, dovresti modificare il codice. – LaTeX

risposta

36

in che modo una classe figlio può sapere (senza dare un'occhiata all'implementazione della classe base) quale ordine (o opzione) è previsto dalla classe padre?

Non c'è modo di "saperlo" quando si sottoclassa e si sovrascrive un metodo. La documentazione corretta è davvero l'unica opzione qui.

Esiste un modo in cui la classe padre può applicare uno dei tre sostituti a tutte le classi derivate?

L'unica opzione qui è evitare il problema. Invece di consentire alla sottoclasse di sovrascrivere il metodo, può essere dichiarato non virtuale e chiamare un metodo virtuale nella posizione appropriata.Ad esempio, se si desidera applicare che sottoclassi "chiamano la versione prima", si potrebbe fare:

public class BaseClass { 
    public void Method() // Non-virtual 
    { 
      // Do required work 

      // Call virtual method now... 
      this.OnMethod(); 
    } 

    protected virtual void OnMethod() 
    { // Do nothing 
    } 
} 

Le sottoclassi possono poi "override" OnMethod, e forniscono funzionalità che accade dopo la 's lavoro "metodo".

Il motivo per cui è necessario è che i metodi virtuali siano progettati per consentire a una sottoclasse di sostituire completamente l'implementazione della classe genitore. Questo è fatto apposta. Se vuoi evitare questo, è meglio rendere il metodo non virtuale.

+1

Mi hai battuto. Questo è il ** problema di bassa classe **. +1 per la soluzione alternativa (che mi hai battuto anche a :), il ** pattern method pattern **. Si potrebbe anche creare l'abstract 'OnMethod' protetto in modo che le classi derivate sappiano che * hanno * per fornire la propria implementazione, e inoltre che non devono chiamare un'implementazione di base (assumendo che non abbia senso istanziare un 'base'). – shambulator

+0

+1 Ciò ti salverà dalla furia dei tuoi colleghi sviluppatori. – Marc

+0

@shambulator - Override è opzionale, quindi probabilmente non è paragonabile a fare l'abstract di 'OnMethod'. È ancora una buona opzione, anche se il tuo esempio è –

1

La risposta breve è no. Non è possibile imporre in quale ordine il bambino chiama il metodo base o se lo chiama affatto.

Tecnicamente queste informazioni devono essere incluse nella documentazione dell'oggetto di base. Se è assolutamente necessario eseguire un codice prima o dopo il codice della classe figlio, è possibile effettuare quanto segue:

1) Creare una funzione non virtuale nella classe base. Chiamiamola MyFunction

2) Creare una funzione virtuale protetta nella classe base. Chiamiamola _MyFunction

3) Le classi derivate hanno esteso il metodo _MyFunction.

4) Avere MyFunction chiamare _MyFunction ed eseguire il codice che deve essere eseguito prima o dopo averlo chiamato.

Questo metodo è brutto e richiederebbe molto codice aggiuntivo, quindi consiglio di mettere un avviso nella documentazione.

+0

Mi chiedo se qualcosa come PostSharp possa far rispettare questo ordine. – FrustratedWithFormsDesigner

0

I requisiti della classe di base devono essere documentati dal progettista della biblioteca. Questo problema è il motivo per cui alcune librerie contengono principalmente classi sigillate.

2

Questo è il motivo per cui ritengo che i metodi virtuali siano pericolosi quando li spedisci in una biblioteca. La verità è che non si sa mai veramente senza guardare la classe base, a volte si deve accendere reflektor, leggere la documentazione o affrontarla con prove ed errori.

Quando la scrittura di codice mi sono sempre stanco di seguire la regola che dice:

classi derivate che l'override del metodo virtuale protetto non sono tenuti a chiamare l'implementazione della classe base. La classe base deve continuare a funzionare correttamente anche se la sua implementazione non viene chiamata.

Questo è preso da http://msdn.microsoft.com/en-us/library/ms229011.aspx, tuttavia questo è per il design di eventi anche se credo di aver letto questo nel libro di linee guida di progettazione Framework (http://www.amazon.com/Framework-Design-Guidelines-Conventions-Libraries/dp/0321246756).

Tuttavia, questo ovviamente non è vero, i moduli Web ASP.NET richiedono ad esempio una chiamata di base su Page_Load.

Quindi, lungo e breve, varia e sfortunatamente non esiste un modo di conoscere istantaneo. Se sono in dubbio ometterò inizialmente la chiamata.

+0

Che meravigliosa risposta. I miei pensieri esatti. Up votato. – Xtro