Tutti i metodi pubblici in una classe astratta devono essere contrassegnati come virtuali?

12

Recentemente ho dovuto aggiornare una classe base astratta su un OSS che stavo usando in modo che fosse più testabile rendendoli virtuali (non potevo usare un'interfaccia poiché ne combinava due). Questo mi ha fatto pensare se dovevo marcare tutti i metodi che mi servivano virtuali, o se dovessi contrassegnare ogni metodo pubblico / proprietà virtuale. Io generalmente sono d'accordo con Roy Osherove che ogni metodo dovrebbe essere reso virtuale, ma io ha trovato questo articolo che mi ha fatto riflettere sul fatto che fosse necessario o meno . Ho intenzione di limitare questo per classi astratte per semplicità, tuttavia (se tutti i metodi pubblici concreti dovrebbero essere virtuali è particolarmente discutibile, ne sono sicuro).

Potrei vedere dove potresti voler consentire ad una sottoclasse di usare un metodo, ma non voglio che sovrascriva l'implementazione. Tuttavia, se ti fidi che Principio di sostituzione di Liskov verrà seguito, allora perché non permetteresti che venga superato? Marcandolo in astratto, stai forzando comunque un certo override, quindi, mi sembra che tutti i metodi pubblici all'interno di una classe astratta debbano essere effettivamente contrassegnati come virtuali.

Tuttavia, volevo chiederti se c'era qualcosa che potrei non pensare. Dovrebbero essere resi virtuali tutti i metodi pubblici all'interno di una classe astratta?

    
posta Justin Pihony 15.04.2012 - 21:35
fonte

5 risposte

8

However, as long as you trust that Liskov's Substitution Principle will be followed, then why would you not allow it to be overriden?

Ad esempio, perché voglio che venga fissata l'implementazione dello scheletro di un algoritmo e che solo le parti specifiche siano (ri) definite da sottoclassi. Questo è ampiamente noto come modello Metodo del modello (enfasi sotto di me):

The template method thus manages the larger picture of task semantics, and more refined implementation details of selection and sequence of methods. This larger picture calls abstract and non-abstract methods for the task at hand. The non-abstract methods are completely controlled by the template method but the abstract methods, implemented in subclasses, provide the pattern's expressive power and degree of freedom. Some or all of the abstract methods can be specialized in a subclass, allowing the writer of the subclass to provide particular behavior with minimal modifications to the larger semantics. The template method (which is non-abstract) remains unchanged in this pattern, ensuring that the subordinate non-abstract methods and abstract methods are called in the originally-intended sequence.

Aggiornamento

Alcuni esempi concreti di progetti su cui ho lavorato:

  1. comunicare con un sistema mainframe legacy tramite varie "schermate". Ogni schermata ha una serie di campi, di nome, posizione e lunghezza fissi, contenenti bit di dati specifici. Una richiesta riempie determinati campi con dati specifici. Una risposta restituisce i dati in uno o più altri campi. Ogni transazione segue la stessa logica di base, ma i dettagli sono diversi su ogni schermo. Abbiamo utilizzato il metodo Template in diversi progetti per implementare lo scheletro fisso della logica di gestione dello schermo, consentendo nel contempo alle sottoclassi di definire i dettagli specifici della schermata.
  2. esportazione / importazione dei dati di configurazione nelle tabelle DB per / da file Excel. Anche in questo caso, lo schema di base per l'elaborazione di un file Excel e l'inserimento / aggiornamento di record DB o il riversamento dei record su Excel sono gli stessi per ogni tabella, ma i dettagli di ogni tabella sono diversi. Quindi, il metodo Template è una scelta molto ovvia per eliminare le duplicazioni di codice e rendere il codice più facile da capire e mantenere.
  3. Generazione di documenti PDF. Ogni documento ha la stessa struttura, ma il loro contenuto è diverso ogni volta, a seconda di molti fattori. Anche in questo caso, Metodo Template semplifica la separazione dello scheletro fisso dell'algoritmo di generazione dai dettagli mutevoli e specifici del caso. Infatti. si applica anche su più livelli qui, in quanto il documento consiste di diverse sezioni , ognuna delle quali consiste di zero o più campi . Il metodo Template è applicato su 3 livelli distinti qui.

Nei primi due casi, l'implementazione legacy originale utilizzava Strategia , risultando in un sacco di codice duplicato, che ovviamente nel corso degli anni sono cresciute sottili differenze qua e là, contenevano molti bug duplicati o leggermente diversi ed era molto difficile da mantenere. Refactoring al metodo Template (e altri miglioramenti, come l'utilizzo delle annotazioni Java) ha ridotto le dimensioni del codice di circa il 40-70%.

Questi sono solo gli esempi più recenti che mi vengono in mente. Potrei citare più casi da quasi tutti i progetti a cui ho lavorato fino ad ora.

    
risposta data 16.04.2012 - 00:54
fonte
2

È perfettamente ragionevole, ea volte desiderabile avere metodi non virtuali in una classe base astratta; solo perché è una classe astratta non significa necessariamente che ogni sua parte dovrebbe essere utilizzabile in modo polimorfico.

Ad esempio, potresti voler usare l'idioma 'Non virtuale polimorfismo', per cui una funzione è chiamata polimorficamente da una funzione membro non virtuale, al fine di garantire che determinate condizioni preliminari o post-condizioni siano soddisfatte prima che venga chiamata la funzione virtuale

class MyAbstractBaseClass
{
protected:
    virtual void OverrideMe() = 0;
public:
    void CallMeFirst();

    void CallMe()
    {
        CallMeFirst();
        OverrideMe();
    }
};
    
risposta data 15.04.2012 - 22:38
fonte
2

È sufficiente che una classe contenga UN metodo virtuale, in modo che la classe diventi astratta. Potresti voler prestare attenzione a quali metodi vuoi virtuali e quali no, in base al tipo di polimorfismo che stai pianificando di utilizzare.

    
risposta data 05.05.2012 - 22:23
fonte
1

Chiediti quale sia l'uso di un metodo non virtuale in una classe astratta. Un tale metodo dovrebbe avere un'implementazione per renderlo utile. Ma se la classe ha un'implementazione, può ancora essere definita una classe astratta? Anche se il linguaggio / compilatore lo consente, ha senso? Personalmente, io non la penso così. Avresti una classe normale con metodi astratti che i discendenti dovrebbero implementare.

La mia lingua principale è Delphi, non C #. In Delphi, se contrassegni un metodo astratto, devi anche contrassegnarlo come virtuale o il compilatore si lamenta. Non ho seguito le ultime modifiche del linguaggio troppo da vicino, ma se le classi astratte sono o verranno a Delphi, mi aspetterei che il compilatore si lamentasse di qualsiasi metodo non virtuale, di qualsiasi metodo privato e di qualsiasi implementazione del metodo per una classe contrassegnata come astratta a livello di classe.

    
risposta data 15.04.2012 - 22:00
fonte
0

However, as long as you trust that Liskov's Substitution Principle will be followed, then >why would you not allow it to be overriden?

Non rendi certi metodi virtuali perché non credi che questo sia il caso. Inoltre, rendendo certi metodi non virtuali, stai segnalando agli ereditari quale metodo (i) deve essere implementato.

Personalmente, spesso realizzerò sovraccarichi di metodo che esistono per comodità non virtuali, in modo che gli utenti della classe possano avere valori predefiniti coerenti e gli implementatori non siano nemmeno in grado di commettere l'errore di interrompere il comportamento implicito.

    
risposta data 16.04.2012 - 00:31
fonte

Leggi altre domande sui tag