Bien sûr, mais nous appelons cette composition et cette délégation . Le modèle de stratégie et l'injection de dépendance peuvent sembler structurellement similaires, mais leurs intentions sont différentes.
Le modèle de stratégie permet de modifier l'exécution du comportement sous la même interface. Je pourrais dire à un canard colvert de voler et le regarder voler avec des ailes. Échangez-le ensuite contre un canard pilote et regardez-le voler avec Delta Airlines. Faire cela pendant que le programme est en cours d'exécution est une chose de modèle de stratégie.
L'injection de dépendances est une technique pour éviter les dépendances de codage en dur afin qu'elles puissent changer indépendamment sans exiger que les clients soient modifiés lorsqu'ils changent. Les clients expriment simplement leurs besoins sans savoir comment ils seront satisfaits. Ainsi, la manière dont ils sont atteints est décidée ailleurs (généralement dans le principal). Vous n'avez pas besoin de deux canards pour utiliser cette technique. Juste quelque chose qui utilise un canard sans savoir ni se soucier de quel canard. Quelque chose qui ne construit pas le canard ou ne le cherche pas, mais qui est parfaitement heureux d'utiliser le canard que vous lui tendez.
Si j'ai une classe de canard en béton, je peux la faire implémenter son comportement à la mouche. Je pourrais même lui faire passer des comportements de voler avec des ailes à voler avec Delta en fonction d'une variable d'état. Cette variable pourrait être un booléen, un int, ou ce pourrait être un FlyBehavior
qui a une fly
méthode qui fait n'importe quel style de vol sans que je doive le tester avec un if. Maintenant, je peux changer de style de vol sans changer de type de canard. Maintenant, les canards colverts peuvent devenir pilotes. C'est la composition et la délégation . Le canard est composé d'un FlyBehavior et il peut lui déléguer des demandes de vol. Vous pouvez remplacer tous vos comportements de canard à la fois de cette façon ou détenir quelque chose pour chaque comportement ou toute combinaison entre les deux.
Cela vous donne tous les mêmes pouvoirs que l'héritage, sauf un. L'héritage vous permet d'exprimer les méthodes Duck que vous remplacez dans les sous-types Duck. La composition et la délégation nécessitent que le canard délègue explicitement aux sous-types dès le départ. C'est beaucoup plus flexible mais cela implique plus de frappe au clavier et Duck doit savoir que cela se produit.
Cependant, beaucoup de gens croient que l'héritage doit être explicitement conçu dès le départ. Et que si ce n'est pas le cas, vous devez marquer vos classes comme scellées / finales pour interdire l'héritage. Si vous adoptez ce point de vue, l'héritage n'a vraiment aucun avantage sur la composition et la délégation. Parce que dans tous les cas, vous devez soit concevoir une extensibilité dès le départ, soit être prêt à démolir les choses plus tard.
Démolir les choses est en fait une option populaire. Sachez simplement qu'il y a des cas où c'est un problème. Si vous avez déployé indépendamment des bibliothèques ou des modules de code que vous n'avez pas l'intention de mettre à jour avec la prochaine version, vous pouvez vous retrouver avec des versions de classes qui ne savent rien de ce que vous êtes en train de faire.
Bien que vouloir démolir les choses plus tard puisse vous libérer de la conception excessive, il est très puissant de pouvoir concevoir quelque chose qui utilise un canard sans avoir à savoir ce que le canard fera réellement lorsqu'il sera utilisé. Ce non-savoir est un truc puissant. Il vous permet d'arrêter de penser aux canards pendant un certain temps et de penser au reste de votre code.
«Pouvons-nous» et «devrions-nous» sont des questions différentes. Privilégier la composition plutôt que l'héritage ne dit pas de ne jamais utiliser l'héritage. Il y a encore des cas où l'héritage a le plus de sens. Je vais vous montrer mon exemple préféré :
public class LoginFailure : System.ApplicationException {}
L'héritage vous permet de créer des exceptions avec des noms descriptifs plus spécifiques sur une seule ligne.
Essayez de faire cela avec la composition et vous obtiendrez un gâchis. En outre, il n'y a aucun risque de problème yo-yo d' héritage car il n'y a pas de données ou de méthodes ici pour réutiliser et encourager le chaînage d'héritage. Tout cela ajoute un bon nom. Ne sous-estimez jamais la valeur d'un bon nom.
Duckbehavior.quackBehavior
et les autres champs de votre code?