Devo usare metodi astratti o virtuali?

8

Se supponiamo che non sia desiderabile che la classe base sia una pura classe di interfaccia e che usi i 2 esempi dal basso, che è un approccio migliore, usando la definizione della classe del metodo astratto o virtuale?

  • Il vantaggio della versione "astratta" è che probabilmente sembra più pulito e costringe la classe derivata a dare un'implementazione che si spera significativa.

  • Il vantaggio della versione "virtuale" è che può essere facilmente richiamato da altri moduli e utilizzato per i test senza aggiungere una serie di framework sottostanti come richiede la versione astratta.

Versione astratta:

public abstract class AbstractVersion
{
    public abstract ReturnType Method1();        
    public abstract ReturnType Method2();
             .
             .
    public abstract ReturnType MethodN();

    //////////////////////////////////////////////
    // Other class implementation stuff is here
    //////////////////////////////////////////////
}

Versione virtuale:

public class VirtualVersion
{
    public virtual ReturnType Method1()
    {
        return ReturnType.NotImplemented;
    }

    public virtual ReturnType Method2()
    {
        return ReturnType.NotImplemented;
    }
             .
             .
    public virtual ReturnType MethodN()
    {
        return ReturnType.NotImplemented;
    }

    //////////////////////////////////////////////
    // Other class implementation stuff is here
    //////////////////////////////////////////////
}
    
posta Dunk 26.02.2013 - 18:47
fonte

5 risposte

14

Il mio voto, se stavo consumando le tue cose, sarebbe per i metodi astratti. Ciò va di pari passo con "fallire presto". Potrebbe essere un problema al momento della dichiarazione aggiungere tutti i metodi (anche se qualsiasi strumento di refactoring decente lo farà in fretta), ma almeno io so quale sia il problema immediatamente e lo risolvo. Preferirei piuttosto che eseguire il debug di 6 mesi e 12 volte il valore delle modifiche in un secondo momento per capire perché improvvisamente stiamo ottenendo un'eccezione non implementata.

    
risposta data 26.02.2013 - 18:56
fonte
22

La versione virtuale è sia soggetta a bug che semanticamente scorretta.

Abstract dice "questo metodo non è implementato qui. devi implementarlo per far funzionare questa classe"

Virtual sta dicendo "Ho un'implementazione predefinita ma puoi cambiarmi se ti serve"

Se il tuo obiettivo finale è la testabilità, le interfacce sono normalmente l'opzione migliore. (questa classe fa x piuttosto che questa classe è una x). Potrebbe essere necessario rompere le lezioni in componenti più piccoli, anche se per far funzionare tutto questo.

    
risposta data 26.02.2013 - 22:50
fonte
3

Dipende dall'uso della tua classe.

Se i metodi hanno un'implementazione "vuota" ragionevole, hai molti metodi e spesso ne sovrascrivi solo alcuni, quindi usare i metodi virtual ha senso. Ad esempio ExpressionVisitor è implementato in questo modo.

Altrimenti, penso che dovresti usare i metodi abstract .

Idealmente, non dovresti avere metodi che non sono implementati, ma in alcuni casi, questo è l'approccio migliore. Ma se decidi di farlo, tali metodi dovrebbero generare NotImplementedException , non restituire alcun valore speciale.

    
risposta data 26.02.2013 - 21:05
fonte
1

Suggerirei di riconsiderare l'esistenza di un'interfaccia separata definita da implementare nella classe base e quindi di seguire l'approccio astratto.

Codice di imaging come questo:

public interface IVersion
{
    ReturnType Method1();        
    ReturnType Method2();
             .
             .
    ReturnType MethodN();
}

public abstract class AbstractVersion : IVersion
{
    public abstract ReturnType Method1();        
    public abstract ReturnType Method2();
             .
             .
    public abstract ReturnType MethodN();

    //////////////////////////////////////////////
    // Other class implementation stuff is here
    //////////////////////////////////////////////
}

Questo risolve questi problemi:

  1. Ora è possibile implementare tutto il codice che utilizza oggetti derivati da AbstractVersion per ricevere invece l'interfaccia di IVersion, il che significa che possono essere più facilmente testati con le unità.

  2. La versione 2 del tuo prodotto può quindi implementare un'interfaccia IVersion2 per fornire funzionalità aggiuntive senza violare il codice dei clienti esistenti.

ad es.

public interface IVersion
{
    ReturnType Method1();        
    ReturnType Method2();
             .
             .
    ReturnType MethodN();
}

public interface IVersion2
{
    ReturnType Method2_1();
}

public abstract class AbstractVersion : IVersion, IVersion2
{
    public abstract ReturnType Method1();        
    public abstract ReturnType Method2();
             .
             .
    public abstract ReturnType MethodN();
    public abstract ReturnType Method2_1();

    //////////////////////////////////////////////
    // Other class implementation stuff is here
    //////////////////////////////////////////////
}

Vale anche la pena di leggere l'inversione delle dipendenze, per evitare che questa classe contenga dipendenze codificate che impediscono un efficace test delle unità.

    
risposta data 04.09.2013 - 16:07
fonte
-2

L'iniezione di dipendenza si basa su interfacce. Ecco un breve esempio. Studente di classe ha una funzione chiamata CreateStudent che richiede un parametro che implementa l'interfaccia "IReporting" (con un metodo ReportAction). Dopo aver creato uno studente, chiama ReportAction sul parametro concreto della classe. Se il sistema è configurato per inviare un'e-mail dopo aver creato uno studente, inviamo una classe concreta che invia un'e-mail nell'implementazione ReportAction, oppure potremmo inviare un'altra classe concreta che invia l'output a una stampante nella sua implementazione ReportAction. Ottimo per il riutilizzo del codice.

    
risposta data 21.05.2015 - 13:19
fonte

Leggi altre domande sui tag