Procedura consigliata per evitare o rifattorizzare il tipo di base di trasmissione nella classe derivata

-2

Ho un esempio dei modelli di progettazione che dimostra il modello dell'adattatore. Ma vedo casting per la classe derivata qui. Non è molto buono Se incontro questo tipo di codice nella mia pratica, qual è il modo migliore per rifattenerlo? O quali principi dovrei seguire per evitare tale situazione?

public class SqlServerLogSaverAdapter : ILogSaver
{
    private readonly SqlServerLogSaver _sqlServerLogSaver = new SqlServerLogSaver();
    public void Save(LogEntry logEntry)
    {
        var simpleEntry = logEntry as SimpleLogEntry;
        if (simpleEntry != null)
        {
            _sqlServerLogSaver.Save(simpleEntry.EntryDateTime,
                simpleEntry.Severity.ToString(),
                simpleEntry.Message);
            return;
        }
        var exceptionEntry = (ExceptionLogEntry)logEntry;
        _sqlServerLogSaver.SaveException(exceptionEntry.EntryDateTime, 
                exceptionEntry.Message,
                exceptionEntry.Exception);
    }
}

ps. Può essere un modello di visitatore che lo risolve ma non ne sono sicuro.

    
posta Vitaly Mosin 19.06.2017 - 10:42
fonte

4 risposte

3

La migliore pratica è seguire i principi SOLID . Nello specifico, il codice dovrebbe dipendere dalle interfacce, non dai dettagli di implementazione.

Il problema qui è che il tuo LogSaver dipende da una particolare implementazione concreta di LogEntry. La soluzione è che LogSaver dipende da un'interfaccia comune. In questo esempio, chiamo quell'interfaccia ISaveableLogEntry .

public interface ISaveableLogEntry
{
    DateTime EntryDateTime { get; set; }
    String   TextToSave { get; }
}

public class SimpleLogEntry : ISaveableLogEntry
{
    public DateTime EntryDateTime { get; set; }
    public String Severity { get; set; }
    public String Message { get; set; }
    public String TextToSave 
    {
        get { return Severity + " " + Message; }
    }
}

public class ExceptionLogEntry : ISaveableLogEntry
{
    public DateTime EntryDateTime {get; set; }
    public Exception Exception { get; set; }
    public String TextToSave 
    {
        get { return this.Exception.Message + " " + this.Exception.StackTrace; }
    }
}

public class SqlServerLogSaverAdapter : ILogSaver
{
    private readonly SqlServerLogSaver _sqlServerLogSaver = new SqlServerLogSaver();

    public void Save(LogEntry logEntry)
    {
        ISaveableLogEntry e = logEntry as ISaveableLogEntry;
        if (e == null) return;
        _sqlServerLogSaver.Save(e.EntryDateTime, e.TextToSave);
    }
}

Con il codice precedente non hai mai bisogno di trasmettere da generale a specifico. Devi solo eseguire il cast da specifico a generale (per ottenere l'interfaccia ISaveableLogEntry ). Se il cast fallisce, la voce non può essere salvata (il che ha senso se l'oggetto non è una voce di registro salvabile). Altrimenti, c'è un solo percorso di codice per salvarlo, poiché ogni SaveableLogEntry sa cosa deve essere salvato.

Come ha sottolineato David, se sei disposto a modificare il prototipo di SqlServerLogSaverAdapter.Save() , puoi anche evitare tutti i casting cambiandolo in

public void Save(ISaveableLogEntry logEntry)

... e filtrando la lista prima di chiamarla. Facile da fare con LINQ:

foreach (ISaveableLogEntry logEntry in _logEntryList.OfType<ISaveableLogEntry>())
{
    logAdapter.Save(logEntry);
}
    
risposta data 19.06.2017 - 20:49
fonte
0

A mio parere, la definizione dell'interfaccia di ILogSaver è errata: quando devi gestire SimpleLogEntry diverso da ExceptionLogEntry , hai bisogno di due funzioni distinte nell'interfaccia invece della singola funzione Save .

    
risposta data 20.06.2017 - 10:55
fonte
-1

In parole semplici, qualsiasi codice che funziona solo con un tipo dovrebbe essere parte di quel tipo, piuttosto che un client. I clienti non dovrebbero preoccuparsi di quale sottotipo particolare di una base o interfaccia astratta ricevono. Quindi, per il tuo esempio specifico, lo rifattore in questo modo:

public class SqlServerLogSaverAdapter : ILogSaver
{
    private readonly SqlServerLogSaver _sqlServerLogSaver =
    new SqlServerLogSaver();
    public void Save(LogEntry logEntry)
    {
        logEntry.saveTo(_sqlServerLogSaver);
    }
}

// ... in LogEntry
    abstract void saveTo(SqlServerLogSaver saver);

// ... in SimpleLogEntry

    void saveTo(SqlServerLogSaver  _sqlServerLogSaver)
    {
            _sqlServerLogSaver.Save(EntryDateTime,
                Severity.ToString(),
                Message);
    }


    //  in ExceptionLogEntry...

    void saveTo(SqlServerLogSaver  _sqlServerLogSaver)
    {
        _sqlServerLogSaver.SaveException(EntryDateTime, Message,
                                         Exception);
    }
}
    
risposta data 19.06.2017 - 11:47
fonte
-2

Poiché il metodo fallirà se viene passato un oggetto ExceptionLogEntry , il parametro methods dovrebbe essere di tipo ExceptionLogEntry in modo che il chiamante sia consapevole di questa dipendenza e non sia necessaria alcuna trasmissione.

    
risposta data 19.06.2017 - 11:42
fonte

Leggi altre domande sui tag