2010-08-23 4 views
7

J'ai un problème de conception que je voudrais résoudre. J'ai une interface, appelons-le IProtocol, qui est implémenté par deux classes distinctes. Nous examinons plus de 600 lignes de code ici. La grande majorité des choses qu'ils font est le même, sauf pour certains domaines spécifiques, comme DiffStuff();Problème de conception - L'héritage est-il la bonne façon de simplifier ce code?

La structure actuelle est quelque chose comme ceci:

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

Et

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

Je suis préoccupé par les erreurs de copier-coller et t Le problème classique de la duplication de code si je garde les deux protocoles séparés. Nous parlons de 600 lignes de code chacune, pas de méthodes simples.

J'envisage de changer la mise en œuvre de Protocol1 hériter de PROTOCOLE2, comme celui-ci (Protocole2 serait la plupart du temps restent les mêmes, sauf que je dois conclure Same1() et Same2() dans des méthodes privées.)

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

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

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

Est-ce la bonne façon de régler ce problème?

Modifier: Beaucoup de gens m'ont aidé avec cette question, merci pour la compréhension claire. Dans mon cas, les deux objets ne sont pas du même type, même si une grande partie de leur implémentation est partagée, donc je suis allé avec Bobby's suggestion pour utiliser la classe de base abstraite, créant de petites méthodes pour encapsuler les changements entre les classes. Merci supplémentaires:

  • jloubert
  • Hans
  • Passant Jeff sternale
+10

Pourquoi ne pas utiliser une classe abstraite qui définit les méthodes partagées et définitions abstraites pour les nécessaires qui ne sont pas mises en œuvre ? – alternative

+0

Jetez un coup d'œil à "Code propre" par Robet C. Martin –

+2

Vous ne devriez faire hériter 'Protocol1' de' Protocol2' que si cela a du sens. Vous devez demander, "est-ce que chaque Protocol1 est aussi une instance de Protocol2, dans le même sens que chaque Chat est un Mammifère?" Si c'est le cas, faites ce que vous pensez. Sinon, j'irais avec une classe de base abstraite, comme indiqué dans la réponse de Bobby. – jloubert

Répondre

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

puis ...

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 
} 

enfin ...

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

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

Pas tout à fait. Il ne sert à rien d'ajouter les méthodes Same1 et Same2, vous les héritez de ProtocolBase. Et DiffStuff() devrait être une méthode virtuelle afin que vous puissiez la remplacer et lui donner un comportement différent.

-1

Je suis d'accord avec MathEpic. Je voudrais utiliser le modèle Template method.

+3

(-1) S'il vous plaît poster vos commentaires en tant que * commentaires *. – DevinB

0

Je ferais mieux de la conception (à mon avis)

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. 
    } 
} 

(C'est en fait plus pseudo-code que bon C#, mais je frappe les points forts)

+1

Protocole1 ne doit pas et hériter de Protocol_common et Protocol_common doit être abstrait? –

+0

D'oh! beaucoup de copy'n'paste –

+0

L'implémentation de MyInterfaceMethod1 pour Protocol1 et Protocol2 diffère légèrement (Protocol2 n'appelle pas réellement DiffStuff), donc ceci devrait être rendu abstrait et être implémenté séparément par classe dérivée aussi. Ou peut-être utiliser des méthodes virtuelles et considérer l'une des implémentations de DiffStuff comme le comportement par défaut. – fletcher

4

Vous n'êtes terriblement près de décrire la template method pattern , qui a un long pedigree comme une solution efficace à des problèmes comme le vôtre. Cependant, vous devriez envisager d'utiliser la composition à la place de l'héritage. The two approaches offer different advantages and disadvantages, mais la composition est souvent (habituellement?) Mieux *:

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(); 
     } 
} 

personnes qui appellent peuvent ensuite passer dans les instances d'objet (strategies) qui fournissent des algorithmes spécialisés pour traiter tout cas est à portée de main:

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

* Voir Patterns I Hate #2: Template Method pour une analyse détaillée explication de la raison pour laquelle la composition est généralement meilleure que l'héritage dans votre cas.

+0

C'est probablement un meilleur ajustement pour un protocole que la composition inversée que j'ai suggérée. –

+0

Petite suggestion que je ferais, est de faire de l'ISpecialHandler une propriété plutôt que de le prendre dans le constructeur. Cela permet aux appelants de votre API de savoir spécifiquement que ISpecialHandler est facultatif. – BFree

0

Vous voudrez peut-être examiner si ce type de travaux modèle:

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(); 
    } 
} 

Vous pouvez dire si cela a du sens en voyant si vous pouvez trouver un bon nom pour la classe « ProtocolHelper ». Si un nom vient naturellement de votre logique, alors c'est un bon moyen de briser la classe. Vous devrez peut-être transmettre certaines dépendances (telles que des champs privés) en tant que paramètres aux méthodes pour que cela fonctionne.

Questions connexes