2010-08-12 6 views
5

Situation:C# problème de conception OO avec priorité des méthodes

Assembly 1 
________________________    ________________________ 
| Class A    |   | Class B    | 
|-----------------------|   |-----------------------| 
| Method someMethod  |---------->| Method otherMethod | 
|      |   |      | 
|_______________________|   |_______________________| 

Assemblée 1 est une application que d'autres développeurs peuvent utiliser. Nous leur donnerons seulement le .dll afin que nous puissions publier des mises à jour de l'application si nous ne changeons pas l'API. Les développeurs ne peuvent pas modifier le framework dans l'assembly 1

Les méthodes sont virtuelles afin que les développeurs puissent remplacer les méthodes pour implémenter leur propre logique si nécessaire. Le problème est qu'un développeur ne peut pas remplacer otherMethod de la classe B, il peut le remplacer, mais la classe A appellera toujours la méthode de la classe B et non la méthode surchargée.

Assembly 1 
________________________    ________________________ 
| Class A    |   | Class B    | 
|-----------------------|   |-----------------------| 
| Method someMethod  |----XX---->| Method otherMethod | 
|      |   |      | 
|_______________________|   |_______________________| 
       \         | 
       \         | 
        \         | 
Assembly 2   \         | 
        \    ________________|_______ 
        \    | Class ExtendedB  | 
         \    |-----------------------| 
         \____________>| Method otherMethod | 
            |      | 
            |_______________________| 

Assemblée 2 nantis d'une référence à l'assemblage 1

classe partielle ne fonctionne pas car il doit être le même ensemble et ne fonctionnera pas plus de 2

Y at-il des modèles de conception pour ce problème? Ou existe-t-il une autre solution avec réflexion ou autre chose?

EDIT Ajout d'un exemple de code:

/* ASSEMBLY 1 */ 

namespace Assembly1 
{ 
    public interface IAService 
    { 
     void TestMethod3(); 
     void TestMethod4(); 
    } 

    public interface IBService 
    { 
     void TestMethod1(); 
     void TestMethod2(); 
    } 

    public class AService : IAService 
    { 
     // Base implementation of AService 
     public virtual void TestMethod3() 
     { 
      //do something 
     } 
     public virtual void TestMethod4() 
     { 
      //do something 
     } 
    } 

    public class BService : IBService 
    { 
     // Base implementation of BService 
     public virtual void TestMethod1() 
     { 
      //do something 
     } 
     public virtual void TestMethod2() 
     { 
      //need to call AService implementation from assembly 2 
     } 
    } 
} 





/* ASSEMBLY 2 */ 
namespace Assembly2 
{ 
    public class NewAService : AService 
    { 
     public override void TestMethod3() 
     { 
      //default implementation which could be overridden 
      base.TestMethod3(); 
     } 

     public override void TestMethod4() 
     { 
      //default implementation which could be overridden 
      //An implementation of IBService Should be called 

      base.TestMethod4(); 
     } 
    } 
} 
+0

Comment appelez-vous la méthode sur la classe B? Est-ce que l'accès est statique? Est-ce que Assembly 2 (ou tout autre assemblage) passe un objet de type ExtendedB à la classe A (injection de dépendance)? – dbemerlin

+1

Pourriez-vous fournir un extrait de code? Les diagrammes ne décrivent pas très bien l'utilisation. – DevinB

+0

Les gens, pourquoi proposons-nous une implémentation d'interface avant même de comprendre pourquoi l'héritage n'autorise pas le polymorphisme? –

Répondre

4

vous devez factoriser

public interface IClassB 
{ 
    void SomeMethod(); 
} 
public Class A 
{ 
    private IClassB myInstanceB = new ClassB(); 

    public ClassA(){} 

    public ClassA(IClass B) 
    { 
     myInstanceB = B; 
    } 

    public void SomeMethod() 
    { 
     myInstanceB.SomeMethod(); 
    } 
} 

public ClassB : IClassB 
{ 
    public void SomeMethod() 
    { 
     // some wicked code here... 
    } 
} 

avec ce refactoring fait, les développeurs peuvent utiliser l'implémentation par défaut en utilisant le constructeur vide. s'ils ont besoin d'une autre logique que celle qu'ils ont juste pour implémenter l'interface IClassB et juste la passer dans l'autre constructeur.

l'utilisation dans l'assemblage 2 serait quelque chose comme ça

public class NewFunctionalityClass : IClassB 
{ 
    public void SomeMethod() 
    { 
     //something else 
    } 
} 
public TestClass() 
{ 
    public void ShowMethod() 
    { 
     var defaultObject = new ClassA(); 
     defaultObject.SomeMethod(); // default implementation 

    var otherObject = new ClassA(new NewFunctionalityClass()); 
    otherObject.SomeMethod(); // here the new implementation will run 
    } 
} 
+0

Je ne vois pas comment cela aide. –

+0

donc vous pouvez facilement ajouter de nouvelles fonctionnalités dans assembly2 – nWorx

+0

J'ai ajouté un exemple de code pour vous ça aide parce que vous ne devrez plus remplacer ou virtualiser les méthodes -> plus simple – nWorx

0

Il semble que vous devrez peut-être utiliser un stragey ou un motif de modèle pour résoudre ce problème.

0

Plusieurs motifs de conception peuvent fonctionner. Voici la liste qui me vient à l'esprit (dans cet ordre) en lisant votre message.

  1. ADAPTER
  2. DECORATOR
  3. TEMPLATE METHOD.
+0

Cela n'explique pas pourquoi l'appel n'est apparemment pas résolu virtuellement. Diagnostiquons avant de prescrire. –

3

En supposant que A appelle une instance de ExtendedB qui est référencé par une variable de type B, et la méthode est marquée virtual dans B et override dans ExtendedB, l'appel doit être polymorphes. Ça devrait marcher. Peut-être pourriez-vous montrer du code.

3

Il est difficile de dire sans le code réel, mais en prenant votre diagramme (en passant, aiment l'art ASCII!) À leur valeur nominale, je suggérerait que la classe A a une référence à une interface plutôt que d'être codée en dur à la classe B.

Comme ceci:

// ----- this is in Assembly 1 ----------- 
public ClassA 
{ 
    public MyInterface myDependentObject; 
} 

public interface MyInterface 
{ 
    void OtherMethod(); 
} 

public class ClassB : MyInterface 
{ 
    public void OtherMethod() 
    { 
     // some code here... 
    } 
} 


// ----- this is in Assembly 2 ----------- 
public class OtherB : MyInterface 
{ 
    public void OtherMethod() 
    { 
     // some code here... 
    } 
} 

Dans votre Assemblée 2, attribuer Autresb à ClasseA:

// ----- this is in Assembly 2 ----------- 
OtherB b = new OtherB(); 
ClassA a = new ClassA { myDependentObject = b }; 

Maintenant, chaque fois que ClassA exécute le myDependentObject.OtherMethod(), il ramassera de l'assemblage 2 au lieu de l'assemblage 1 définitions .

HTH ...

+1

Avoir une référence à une classe de base n'est pas codé en dur. Cela devrait fonctionner très bien, donc je ne vois aucune raison de compliquer cela avec les interfaces, surtout quand on ne sait pas encore pourquoi ça ne marche pas déjà. –

+0

@Steven, le PO demandait un modèle de conception ou une approche de meilleure pratique. Dans ce cas, je continuerais à utiliser des interfaces. Aussi, je pense que c'est assez évident pourquoi cela ne fonctionne pas - ClassA établit une dépendance dure à ClassB quelque part dans l'Assemblée 1. En d'autres termes, nous avons une question de «séparation des préoccupations» ici. Interfaces est la meilleure approche pour gérer ces cas, car elle libère la dépendance et permet de les résoudre plus loin dans le processus (c'est-à-dire dans l'assembly 2 par exemple). – code4life

+0

Je pensais que la meilleure pratique est d'utiliser l'héritage quand vous voulez réellement l'héritage, comme nous le faisons ici. Mais ce qui me préoccupe le plus, c'est que, d'après tout ce que nous avons entendu, l'héritage avec une méthode virtuelle aurait dû mener au polymorphisme, mais ce n'est pas le cas. Je voudrais comprendre pourquoi, avant de punir l'héritage et de contourner le problème. Le problème décrit est * non * reproductible. –

1

Pouvez-vous fournir un code réduit de l'exemple? J'apprécie l'art ascii, mais je soupçonne que le problème peut être dans la façon dont A obtient le B sur lequel il travaille. Si c'est l'instanciation de sa propre copie, il est impossible d'appeler la méthode substituée dans la classe dérivée. Cela dit, je suis également d'accord que le refactoring d'interface suggéré par d'autres est la voie à suivre. Permettre à A de fonctionner à partir de l'interface IB, il doit donc utiliser un implémenteur fourni de IB, plutôt que de générer le sien. De cette façon, cela n'a pas d'importance si l'objet fourni est un B, une sous-classe de B, ou quelque chose d'autre entièrement. Il rend également les sous-classes A, B et Bs plus testables, ce qui est une bonne chose.

+0

Non, ce n'est vraiment pas le cas. Parfois, vous voulez hériter de sorte que vous pouvez sélectivement remplacer certaines méthodes et pas d'autres, et ainsi vous pouvez également sélectivement vous intégrer à l'implémentation de base. Les interfaces sont une bonne chose, mais elles ne sont pas un remplacement direct de l'héritage, et le fait d'y passer ne nous donne aucune idée de la raison pour laquelle l'héritage n'était pas polymorphe. –