Le classi di base dell'utilità dovrebbero essere evitate

1

Sapendo che C # non supporta l'ereditarietà multipla, è considerata una cattiva forma scrivere una classe di utilità?

Alcune riflessioni iniziali:

  • Potrei creare un'interfaccia e una concrezione, ma poi l'implementazione non è portabile
  • Se lo sviluppatore deve estendere la classe, ad esempio, un pattern di decoratore, potrebbe implementare questa nuova classe ed ereditare la classe base nella nuova classe

Modifica :

NB. Qui non sto parlando dell'eredità della vaniglia, ad es. barboncino < - cane < - mammifero < - animale

Più questo tipo di cose (sollevate da qui ):

    using log4net;
    using log4net.Config;

    public class LogTest2
    {
        private static readonly ILog logger = 
              LogManager.GetLogger(typeof(LogTest2));

    static LogTest2()
    {
        DOMConfigurator.Configure();
    }

    static void Main(string[] args)
    {
        logger.Debug("Here is a debug log.");
        logger.Info("... and an Info log.");
        logger.Warn("... and a warning.");
        logger.Error("... and an error.");
        logger.Fatal("... and a fatal error.");
    }
}

Se dovessi rilasciare questo, per esempio, un servizio WCF, sarebbe opportuno che la variabile membro privata, il costruttore e forse alcuni metodi wrapper siedano in una classe base per risparmiare ingombrare il codice del servizio (e renderlo riutilizzabile ).

Finora, così noioso.

Tuttavia:

Che cosa succede se volessi aggiungere qualche altro tipo di registrazione non supportato da log4net? Per esempio. Acme Corp fornisce una classe a iMessage del team di supporto in caso di problemi.

Classe base

Se avessi implementato la roba di log4net in una classe base, dovrei quindi creare una nuova classe base master che supporti entrambe le classi e ereditarlo.

Interfaccia

Potrei implementare un'interfaccia ma la concrezione sarebbe ancora nel codice di servizio che stiamo cercando di evitare. Naturalmente la classe di Acme Corp ora può entrare subito, ma stiamo implementando due serie di codice che fanno cose molto simili in modi diversi.

    
posta Robbie Dee 13.03.2014 - 17:30
fonte

3 risposte

15

Penso che tu sia sulla strada di una forma molto spesso vista di un uso errato dell'eredità. Se si desidera una classe X che utilizza un tipo di registratore intercambiabile, iniettare un oggetto di tipo ILog passandolo come parametro costruttore. Se il tuo logger ha bisogno di un'inizializzazione specifica, crea un logger specifico, ereditato da ILog . Non seppellire l'inizializzazione del logger in una sorta di classe base artificiale A di X , nel tentativo di rendere questo riutilizzabile. Quest'ultimo funzionerebbe effettivamente contro la riusabilità (e la testabilità) di X , e anche contro la riutilizzabilità del tuo logger specifico al di fuori del contesto di X .

    
risposta data 14.03.2014 - 10:13
fonte
1

I could create an interface and a concretion, but then the implementation isn't portable

Esiste un compromesso tra l'uso di una classe sigillata (o una classe con un numero finito di sottoclassi) e interfacce. Con una classe sigillata, hai la stessa implementazione ovunque nel programma. Con un'interfaccia, ottieni la flessibilità di avere implementazioni alternative, ma devi sperare che ogni implementazione sia corretta; una parte specifica del programma potrebbe utilizzare l'implementazione A che è corretta, mentre un'altra parte può fare affidamento sulla stessa interfaccia ma utilizzare l'implementazione B buggy.

Quindi, no, avere una dipendenza da una classe concreta non è intrinsecamente una brutta cosa.

(Come per le classi non sigillate, queste si comportano in modo simile alle interfacce in quanto ci si deve fidare che tutte le sottoclassi siano corrette, ma non sono flessibili come le interfacce e corrono il rischio di Problema di classe base fragile ).

Should the developer need to extend the class in say, a decorator pattern, they could implement this new class and inherit the base class in their new class

Se hai bisogno di questo tipo di estensibilità, utilizza un'interfaccia. Dovresti proibire l'ereditarietà a meno che non disegni la tua classe per questo (e probabilmente non vuoi farlo se puoi evitarlo del tutto, perché la composizione è, beh, più componibile.)

    
risposta data 13.03.2014 - 17:51
fonte
0

Consiglierei l'interfaccia e la concrezione. L'implementazione può ancora essere trasferita attraverso l'uso della delega, es. un'altra classe che implementa l'interfaccia può ottenere una sospensione di un'istanza della versione concreta e delegare ad essa quando ha bisogno di utilizzare la funzionalità concreta. Ciò fornisce una maggiore flessibilità se ci si aspetta che l'implementazione / le sottoclassi debbano ereditare da più fonti diverse.

    
risposta data 13.03.2014 - 17:45
fonte

Leggi altre domande sui tag