Il existe trois méthodes courantes, AFAIK, pour implémenter la réutilisabilité en matière de POO
- Héritage: généralement pour représenter est une relation (un canard est un oiseau)
- Composition: généralement pour représenter une relation a (une voiture a un moteur)
- Traits (par exemple le mot-clé trait en PHP): ... pas vraiment sûr de cela
Bien qu'il me semble que les traits peuvent implémenter des relations has-a et is-a, je ne sais pas vraiment à quel type de modélisation il était destiné. Pour quel genre de situations les traits ont-ils été conçus?
object-oriented-design
Extrakun
la source
la source
Réponses:
Les traits sont une autre façon de composer. Considérez-les comme un moyen de composer toutes les parties d'une classe au moment de la compilation (ou du temps de compilation JIT), en assemblant les implémentations concrètes des parties dont vous aurez besoin.
Fondamentalement, vous souhaitez utiliser des traits lorsque vous vous retrouvez à créer des classes avec différentes combinaisons de fonctionnalités. Cette situation se présente le plus souvent pour les personnes qui écrivent des bibliothèques flexibles pour les autres à consommer. Par exemple, voici la déclaration d'une classe de test unitaire que j'ai écrite récemment en utilisant ScalaTest :
Les frameworks de tests unitaires ont une tonne d'options de configuration différentes, et chaque équipe a des préférences différentes sur la façon dont elle veut faire les choses. En plaçant les options dans des traits (qui sont mélangés dans
with
Scala), ScalaTest peut offrir toutes ces options sans avoir à créer des noms de classe commeWordSpecLikeWithMatchersAndFutures
, ou une tonne d'indicateurs booléens d'exécution commeWordSpecLike(enableFutures, enableMatchers, ...)
. Cela facilite le respect du principe d'ouverture / fermeture . Vous pouvez ajouter de nouvelles fonctionnalités et de nouvelles combinaisons de fonctionnalités, simplement en ajoutant un nouveau trait. Cela facilite également le respect du principe de ségrégation d'interface , car vous pouvez facilement mettre des fonctions qui ne sont pas universellement nécessaires dans un trait.Les traits sont également un bon moyen de mettre du code commun dans plusieurs classes qui n'ont pas de sens pour partager une hiérarchie d'héritage. L'hérédité est une relation très étroitement liée, et vous ne devriez pas payer ce coût si vous pouvez l'aider. Les traits sont une relation beaucoup plus lâche couplée. Dans mon exemple ci-dessus, j'avais l'habitude
MyCustomTrait
de partager facilement une implémentation de base de données fictive entre plusieurs classes de test autrement non liées.L'injection de dépendances atteint plusieurs des mêmes objectifs, mais au moment de l'exécution en fonction des entrées de l'utilisateur plutôt qu'au moment de la compilation en fonction des entrées du programmeur. Les traits sont également destinés davantage aux dépendances qui font sémantiquement partie de la même classe. Vous êtes en quelque sorte assembler les parties d'une classe plutôt que de faire des appels à d'autres classes avec d'autres responsabilités.
Les frameworks d' injection de dépendances atteignent plusieurs des mêmes objectifs au moment de la compilation sur la base des entrées du programmeur, mais constituent en grande partie une solution de contournement pour les langages de programmation sans prise en charge appropriée des traits. Les traits apportent ces dépendances dans le domaine du vérificateur de type du compilateur, avec une syntaxe plus propre, avec un processus de construction plus simple, qui établit une distinction plus claire entre les dépendances à la compilation et à l'exécution.
la source
with
opérateur est ce qui les différencie,with
c'est une opération de composition. Et oui, les traits remplacent DI sans avoir besoin d'être définis au point d'entrée du code. C'est l'une des choses qui les rend préférables à mon avis.