2010-08-23 13 views
7

Ho un problema di progettazione che vorrei risolvere. Ho un'interfaccia, chiamiamolo IProtocol, che è implementato da due classi separate. Stiamo guardando oltre 600 linee di codice qui. La stragrande maggioranza delle cose che fanno è la stessa, tranne per alcune aree specifiche, come DiffStuff();Problema di progettazione - L'ereditarietà è il modo giusto per semplificare questo codice?

struttura attuale è qualcosa di simile:

public class Protocol1 : IProtocol 
{ 
    MyInterfaceMethod1() 
    { 
    Same1(); 
    DiffStuff(); 
    Same2(); 
    } 
} 

E

public class Protocol2 : IProtocol 
{ 
    MyInterfaceMethod1() 
    { 
    Same1(); 
    Same2(); 
    } 
} 

Mi preoccupo di avere errori di copia-incolla t è un classico problema di duplicazione del codice se tengo separati i due protocolli. Stiamo parlando di 600 righe di codice complete ciascuna, non di semplici metodi.

Sto pensando di cambiare l'implementazione di protocollo1 ereditare da Protocollo2, come questo (Protocollo2 sarebbe per lo più rimanere lo stesso, tranne che avrei dovuto avvolgere Same1() e Same2() in metodi privati.)

public class Protocol1 : Protocol2 
{ 
    void Same1() 
    { 
    base.Same1(); 
    } 

    void Same2() 
    { 
    base.Same2(); 
    } 

    MyInterfaceMethod1() 
    { 
    Same1(); 
    DiffStuff(); 
    Same2(); 
    } 
} 

È questo il modo giusto per affrontare questo problema?

Modifica: Molte persone mi hanno aiutato con questa domanda, grazie per la chiara comprensione. Nel mio caso, i due oggetti non sono dello stesso tipo, anche se gran parte della loro implementazione è condivisa, quindi sono andato con Bobby's suggestion per usare la classe base astratta, creando piccoli metodi per incapsulare le modifiche tra le classi. Ulteriori Grazie a:

  • jloubert
  • Hans Passant
  • Jeff sternale
+10

perché non utilizzare una classe astratta che definisce i metodi condivisi e definizioni astratte per i più necessari che non implementati ? – alternative

+0

Dai un'occhiata a "Clean code" di Robet C. Martin –

+2

Dovresti solo rendere 'Protocol1' ereditato da' Protocol2' se questo ha effettivamente senso. Hai bisogno di chiedere, "ogni Protocollo1 è anche un'istanza del Protocollo2, nello stesso senso in cui ogni Gatto è un Mammifero?" Se lo sono, quindi fai ciò che stai pensando. Altrimenti, andrei con una classe base astratta, come mostrato nella risposta di Bobby. – jloubert

risposta

7
/// <summary> 
    /// IProtocol interface 
    /// </summary> 
    public interface IProtocol 
    { 
     void MyInterfaceMethod1(); 
     void Same1(); 
     void Same2(); 
    } 

poi ...

public abstract class ProtocolBase : IProtocol 
{ 
    #region IProtocol Members 

    public void MyInterfaceMethod1() 
    { 
     // Implementation elided... 
    } 

    public void Same1() 
    { 
     // Implementation elided... 
    } 

    public void Same2() 
    { 
     // Implementation elided... 
    } 

    public abstract void DiffStuff(); 

    #endregion 
} 

finalmente ...

public sealed class Protocol1 : ProtocolBase 
{ 
    public override void DiffStuff() 
    { 
     // Implementation elided... 
    } 
} 

public sealed class Protocol2 : ProtocolBase 
{ 
    public override void DiffStuff() 
    { 
     // Implementation elided... 
    } 
} 
5

Non proprio. Non ha senso aggiungere i metodi Same1 e Same2, ereditali da ProtocolBase. E DiffStuff() dovrebbe essere un metodo virtuale in modo da poterlo sovrascrivere e dargli un comportamento diverso.

-1

Sono d'accordo con MathEpic. Vorrei utilizzare il modello Template method.

+3

(-1) Si prega di inviare i vostri commenti come * commenti *. – DevinB

0

ho una migliore progettazione (a mio parere)

public abstract class Protocol_common : IProtocol 
{ 
    MyInterfaceMethod1() 
    { 
    Same1(); 
    DiffStuff(); 
    Same2(); 
    } 

    abstract void DiffStuff(); 

} 

public class Protocol1 : Protocol_common 
{ 
    DiffStuff() 
    { 
     /// stuff here. 
    } 
} 

public class Protocol2 : Protocol_common 
{ 
    DiffStuff() 
    { 
     /// nothing here. 
    } 
} 

(Che in realtà è più pseudo-codice di corretta C#, ma mi ha colpito i punti alti)

+1

Il Protocollo1 e l'ereditarietà da Protocol_common e Protocol_common non dovrebbero essere astratti? –

+0

D'oh! molto copy'n'paste –

+0

L'implementazione di MyInterfaceMethod1 per Protocol1 e Protocol2 differisce leggermente (Protocol2 in realtà non chiama DiffStuff), quindi questo dovrebbe essere reso astratto e implementato separatamente anche per classe derivata. O forse utilizzare metodi virtuali e considerare una delle implementazioni DiffStuff come comportamento predefinito. – fletcher

4

Sei terribilmente vicino a descrivere la template method pattern , che ha un lungo pedigree come soluzione efficace per problemi come il tuo.

Tuttavia, si dovrebbe considerare l'utilizzo della composizione anziché dell'ereditarietà. The two approaches offer different advantages and disadvantages, ma la composizione è spesso (di solito?) Meglio *:

public class Protocol { 

     private ISpecialHandler specialHandler; 

     public Protocol() {} 

     public Protocol(ISpecialHandler specialHandler) { 
      this.specialHandler = specialHandler; 
     } 

     void Same1() {} 
     void Same2() {} 

     public void DoStuff() { 
      this.Same1(); 
      if (this.specialHandler != null) { 
       this.specialHandler.DoStuff(); 
      } 
      this.Same2(); 
     } 
} 

chiamanti può quindi passare a istanze di oggetti (strategies) che forniscono algoritmi specializzati per gestire qualunque caso è a portata di mano:

Protocol protocol1 = new Protocol(new DiffStuffHandler()); 
protocol1.DoStuff(); 

* Vedi Patterns I Hate #2: Template Method per una dettagliata spiegazione del perché la composizione è solitamente migliore dell'eredità nel tuo caso.

+0

Questo è probabilmente un adattamento migliore per un protocollo rispetto alla composizione invertita che ho suggerito. –

+0

Il piccolo suggerimento che farei è di rendere ISpecialHandler una proprietà piuttosto che prenderla nel costruttore. Ciò significa che i chiamanti della tua API sanno specificamente che ISpecialHandler è facoltativo. – BFree

0

Si potrebbe prendere in considerazione se questo tipo di modello funziona:

public class ProtocolHelper 
{ 
    public void Same1() {} 
    public void Same2() {} 
} 

public class Protocol1 : IProtocol 
{ 
    private readonly ProtocolHelper _helper = new ProtocolHelper(); 

    void MyInterfaceMethod1() 
    { 
     _helper.Same1(); 
     DiffStuff(); 
     _helper.Same2(); 
    } 
} 

Si può dire se questo ha un senso per vedere se si può trovare con un buon nome per la classe del 'ProtocolHelper'. Se un nome deriva naturalmente dalla tua logica, allora questo è un buon modo per rompere la classe. Potrebbe essere necessario passare alcune dipendenze (come i campi privati) come parametri ai metodi per farlo funzionare.

Problemi correlati