Guida con la progettazione della gerarchia dell'ereditarietà

3

Sto riscontrando problemi nella progettazione di una gerarchia di ereditarietà.

Nella figura:

Classe base:

  • Questo utilizza il metodo Template.
  • Contiene la logica / l'algoritmo di base chiama metodi virtuali / astratti

Tipi di derivati:

  • Queste classi forniscono un'implementazione concreta dei metodi virtuali / astratti previsti dalla Base.

Condizioni:

  1. Ho un metodo virtuale (Esegui).
  2. Il metodo Run predefinito utilizza un altro metodo virtuale (Proc).
  3. Se una classe derivata utilizza il comportamento Run predefinito, deve implementare Proc.
  4. Se una classe derivata sovrascrive il comportamento Esegui, non dovrebbe preoccuparsi dell'esistenza di Proc, figuriamoci sovrascrivere.

Hopresoinconsiderazionel'ideadirendereRun()astrattoelasciarecheogniclassederivatagestiscalapropria.Mapoifiniròconilcodiceduplicato.

Sto usando un esempio semplificato qui.

Nel mio progetto attuale, ho diversi metodi virtuali "Run", ognuno dei quali chiama diversi metodi "Proc".

Per questo motivo, ho un sacco di metodi "Proc" virtuali con "lanciare la nuova NotImplementedException ()"

Creare un nuovo tipo derivato è anche confuso, perché il codificatore potrebbe non sapere quali metodi virtuali devono essere implementati / possono essere ignorati / etc.

Ho preso in considerazione l'utilizzo di un modello di strategia, ma dal momento che ho diversi metodi "Esegui", ognuno di questi dovrebbe essere una strategia, che sembra eccessivo / eccessivo ingaggio.

C'èunmodopersemplificarequesto?

[Aggiornamento1]

Eccounesempiopiùvicinoaltipodimetodiconcuistolavorando

E immagina ancora un altro metodo:

    
posta jayars 08.01.2014 - 04:47
fonte

2 risposte

2

Hai creato il tuo problema inutile qui.

  1. Tutto deve essere eseguibile
  2. L'implementazione predefinita di esecuzione richiede proc
  3. Chiunque implementa il proprio run può ignorare proc (ma tutto quel codice inutile adorna ancora la classe e qualsiasi discendente).
  4. La tua implementazione concreta di lancio di proc consente a di configurarsi per gli errori di runtime. Perché farlo?

OK, quindi volevi essere d'aiuto fornendo un'implementazione predefinita ma ti sei imbullonato nei dettagli di implementazione che conosci non sarà sempre desiderato. Dici di voler evitare la duplicazione del codice, ma hai ingombrato la tua gerarchia con un codice che diventerà obsoleto non appena qualcuno lo estenderà.

Vi esorto vivamente a fare il percorso dell'interfaccia / astrazione-base-classe qui:

  1. Classe base astratta eseguibile
  2. Classe base astratta proccer
  3. Crea l'implementazione concreta di proc-calling di eseguibile .
  4. Ove possibile (anche nei metodi di classe proc-caller ) fai riferimento a eseguibili e non a chiamanti proc ).

Quindi l'implosione run di default può essere utilizzata dove desiderato ma può essere eliminata senza penalità. Inoltre, in questo modo le persone sono costrette ad implementare proc se si mischiano in proc-caller ma non devi scrivere la versione "Non implementata" di proc - non possono compilare il codice se non implementano proc . Perché creare quella trap runtime quando puoi richiedere loro di sistemare le cose al momento della compilazione?

Se pensi ci siano altri comportamenti utili che dovrebbero essere disponibili, puoi evitare la duplicazione del codice fornendo "interfacce" nello stesso modo in cui ho mostrato per proc . Chiunque lo desideri può combinarlo.

    
risposta data 08.01.2014 - 09:40
fonte
3

Perché non aggiungere un altro livello nella tua gerarchia?

public class Base
{
    public virtual void Run()
    {
        // some code
    }
}

public class ProcBase : Base
{
    public sealed override void Run() // make sure it cannot be overriden
    {
        Proc();
        base.Run();
    }

    public virtual void Proc()
    { 
        // some code
    }
}

public class Type1 : Base // same with Type4
{
    public override void Run()
    {
        base.Run();
    }
}

public class Type2 : ProcBase // same with Type3
{
    public override void Proc()
    {
        base.Proc();
    }
}

Inoltre, se questa gerarchia cambia tra diversi Runs , allora l'utilizzo del modello di strategia diventa inevitabile.

    
risposta data 08.01.2014 - 08:02
fonte

Leggi altre domande sui tag