J'ai une classe: A
qui est un composé d'un certain nombre de classes plus petites, B
, C
et D
.
B
, C
Et D
mettre en œuvre des interfaces IB
, IC
et ID
respectivement.
Depuis A
prend en charge toutes les fonctionnalités de B
, C
et D
, A
implémente IB
, IC
ainsi ID
que, mais cela conduit malheureusement à beaucoup de réacheminement dans la mise en œuvre deA
Ainsi:
interface IB
{
int Foo {get;}
}
public class B : IB
{
public int Foo {get {return 5;}}
}
interface IC
{
void Bar();
}
public class C : IC
{
public void Bar()
{
}
}
interface ID
{
string Bash{get; set;}
}
public class D: ID
{
public string Bash {get;set;}
}
class A : IB, IC, ID
{
private B b = new B();
private C c = new C();
private D d = new D();
public int Foo {get {return b.Foo}}
public A(string bash)
{
Bash = bash;
}
public void Bar()
{
c.Bar();
}
public string Bash
{
get {return d.Bash;}
set {d.Bash = value;}
}
}
Existe-t-il un moyen de se débarrasser de cette redirection de passe-partout A
? B
, C
et D
tous implémentent des fonctionnalités différentes mais communes, et j'aime que cela A
implémente IB
, IC
et ID
parce que cela signifie que je peux passer en A
soi en tant que dépendance correspondant à ces interfaces, et ne pas avoir à exposer les méthodes d'assistance internes.
c#
design-patterns
patterns-and-practices
composition
Nick Udell
la source
la source
A
mise en œuvreIB
,IC
etID
c'est qu'il semble plus sensé d'écrirePerson.Walk()
plutôt quePerson.WalkHelper.Walk()
, par exemple.Réponses:
Ce que vous recherchez est communément appelé mixins . Malheureusement, C # ne les prend pas nativement en charge.
Il existe peu de solutions de contournement: un , deux , trois et bien d'autres.
En fait, j'aime vraiment le dernier. L'idée d'utiliser une classe partielle générée automatiquement pour générer le passe-partout est probablement la solution la plus proche que vous pouvez trouver:
la source
Bien qu'il ne réduise pas le passe-partout dans le code lui-même, Visual Studio 2015 est désormais livré avec une option de refactoring pour générer automatiquement le passe-partout pour vous.
Pour l'utiliser, créez d'abord votre interface
IExample
et votre implémentationExample
. Créez ensuite votre nouvelle classeComposite
et faites-la hériterIExample
, mais n'implémentez pas l'interface.Ajoutez une propriété ou un champ de type
Example
à votreComposite
classe, ouvrez le menu d'actions rapides sur le jetonIExample
dans votreComposite
fichier de classe et sélectionnez «Implémenter l'interface via« Exemple »» où «Exemple» dans ce cas est le nom du champ ou de la propriété.Vous devez noter que même si Visual Studio générera et redirigera pour vous toutes les méthodes et propriétés définies dans l'interface vers cette classe d'assistance, il ne redirigera pas les événements au moment où cette réponse a été publiée.
la source
Votre classe en fait trop, c'est pourquoi vous devez implémenter autant de passe-partout. Vous dites dans les commentaires:
Je ne suis pas d'accord. L'acte de marcher est susceptible d'impliquer de nombreuses règles et n'appartient pas à la
Person
classe - il appartient à uneWalkingService
classe avec unewalk(IWalkable)
méthode où Personne met en œuvreIWalkable
.Gardez toujours à l'esprit les principes SOLID lorsque vous écrivez du code. Les deux plus applicables ici sont la séparation des préoccupations (extraire le code de marche vers une classe distincte) et la séparation des interfaces (diviser les interfaces en tâches / fonctions / capacités spécifiques). Il semble que vous puissiez avoir une partie du I avec vos multiples interfaces, mais que vous annulez ensuite tout votre bon travail en faisant en sorte qu'une seule classe les implémente.
la source
walk(IWalkable)
méthode, je ne l'aime pas personnellement car alors les services de marche deviennent la responsabilité de l'appelant, et non de la personne, et la cohérence n'est pas garantie. Tout appelant devrait savoir quel service utiliser pour garantir la cohérence, alors que le conserver dans la classe Person signifie qu'il doit être modifié manuellement pour que le comportement de marche diffère, tout en permettant l'inversion des dépendances.Ce que vous recherchez, c'est un héritage multiple. Cependant, ni C # ni Java ne l'ont.
Vous pouvez étendre B à partir de A. Cela éliminera le besoin d'un champ privé pour B ainsi que la colle de redirection. Mais vous ne pouvez le faire que pour l'un des B, C ou D.
Maintenant, si vous pouvez faire en sorte que C hérite de B, et D de C, alors il vous suffit d'avoir A étendre D ...;) - juste pour être clair cependant, je ne l'encourage pas vraiment dans cet exemple car il n'y a aucune indication pour ça.
En C ++, vous seriez en mesure de faire hériter A de B, C et D.
Mais l'héritage multiple rend les programmes plus difficiles à raisonner et a ses propres problèmes; De plus, il existe souvent un moyen propre de l'éviter. Pourtant, parfois sans cela, il faut faire des compromis de conception entre la répétition du passe-partout et la façon dont le modèle d'objet est exposé.
C'est un peu comme si vous essayez de mélanger composition et héritage. S'il s'agissait de C ++, vous pourriez utiliser une solution d'héritage pure. Mais comme ce n'est pas le cas, je recommanderais d'adopter la composition.
la source