Utilisation de mixins dans Dart

Plusieurs fois, une question a été posée par des collègues qu'il n'est pas clair pourquoi les mixins (impuretés) dans le langage Dart sont nécessaires. J'ai décidé de voir ce qui se trouve sur Internet à ce sujet. Au grand dam des articles qui ont pu être trouvés, ils parlent principalement de la façon d'utiliser les impuretés, mais ils n'expliquent pas pourquoi ils sont nécessaires, auquel cas leur utilisation est plus préférable que l'héritage ordinaire ou la mise en œuvre d'interfaces. Cet article est une tentative pour combler cette lacune.


Malgré le fait qu'il y ait suffisamment d'articles sur Internet sur le sujet des impuretés dans Dart et Flutter, ils n'apportent pas de clarté à mon avis parce que les exemples donnés montrent la mécanique pure de construction de classes avec des impuretés, ce qui est loin d'être raisonnable et ne démontre donc pas la portée réelle de leur application . En particulier, j'ai rencontré un tel exemple . Nous avons:


class Animal {} class Dog {} class Cat {} 

Et pour une raison quelconque, nous voulions obtenir un animal qui a les propriétés des chats et des chiens en même temps. Dans ce cas, nous pouvons le faire:


 class CatDog extends Animal with Cat, Dog {} 

Cet exemple comporte au moins deux questions:


  • pourquoi avons-nous besoin d'un croisement entre un chat et un chien?
  • Pourquoi un chat et un chien n'héritent-ils pas d' Animal ? N'est-ce pas des animaux?

Dans le même temps, pourquoi les impuretés sont-elles néanmoins nécessaires? Cela reste un mystère.


À mon humble avis, afin de comprendre la signification des impuretés, il faut commencer l'examen de la question avec la relation de l'héritage. Le principal point d'héritage dans la POO est qu'une entité est une variation d' une autre entité. Par exemple, le est une variation de la ou le est une variation de l' . Et c'est précisément ce qui devrait être le facteur déterminant dans la construction d'une hiérarchie de classes.


Si nous regardons l'héritage d'un point de vue différent, nous verrons que le hérite des propriétés de la , et le hérite des propriétés de l' . Si vous ne faites pas attention à la logique, alors, purement techniquement, vous voudrez peut-être hériter des propriétés de plusieurs entités différentes. Pour ce faire, certains langages de programmation prennent en charge l' héritage multiple .


L'héritage multiple est critiqué pour un certain nombre de lacunes (voir Wikipedia ), de nombreux langages de programmation n'utilisent donc pas du tout l'héritage multiple, mais utilisent un mécanisme pour implémenter des interfaces et / ou des impuretés. Et, du point de vue de la logique, les constructions résultant de l'héritage multiple ne sont pas faciles à comprendre.


Pour comprendre le matériel suivant, il est nécessaire de rappeler certains concepts de la logique élémentaire. En particulier, les concepts de propriétés essentielles et non essentielles . Les propriétés essentielles des objets sont celles dues à la présence desquelles il se réfère à une classe particulière d'objets. Les propriétés non essentielles d'un objet sont celles dont la présence, l'absence ou des valeurs spécifiques n'affectent pas l'objet appartenant à une certaine classe d'objets. Par exemple, la forme d'un rectangle est une propriété essentielle de cette figure, car si nous modifions cette forme (supprimons ou ajoutons un côté ou modifions les angles), alors le rectangle cessera d'être un rectangle. Mais si vous redimensionnez le rectangle, il restera toujours un rectangle. Par conséquent, les dimensions sont une propriété insignifiante.


La construction d'une hiérarchie de classes est généralement basée sur l'ajout de propriétés essentielles à la classe parente. Par exemple


 abstract class Shape { void draw(); } class Rectangle extends Shape { @override void draw() { print('Draw rectangle'); } } class Circle extends Shape { @override void draw() { print('Draw circle'); } } 

La base de cette hiérarchie est la propriété essentielle de la forme de la figure.


Un autre exemple:


 abstract class Widget { void render(); } class Container extends Widget { @override void render() { print('Renders container'); } } class Text extends Widget { @override void render('Render text'); } 

Une propriété essentielle ici est le but du widget.


Supposons maintenant que nous devions ajouter des propriétés non essentielles à nos entités. Une telle propriété, par exemple, est la couleur. Souhaitons maintenant coloriser certaines formes et widgets.


Pour ce faire, vous pouvez bien sûr utiliser l'héritage et implémenter d'abord les PaintableWidget PaintableShape et PaintableWidget . Mais cela n'est pas pratique, car, d'une part, nous devrons dupliquer l'implémentation de la fonctionnalité de coloration dans les deux hiérarchies, et, d'autre part, pour chaque figure et widget que nous voulons coloriser, nous devrons implémenter de nouvelles classes, par exemple, PaintableRect et PaintableContainer .


Vous pouvez utiliser le mécanisme pour implémenter des interfaces. Ensuite, nous obtenons quelque chose comme ceci:


 enum Color {red, yellow, green} abstract class Paintable { void paint(Color color); Color get color; } class PaintableRect extends Rectangle implements Paintable { Color _color; @override void paint(Color color) {_color = color;} @override Color get color => _color; } class PaintableContainer extends Container implements Paintable { Color _color; @override void paint(Color color) {_color = color;} @override Color get color => _color; } 

Comme vous pouvez le voir, ce n'est pas non plus la meilleure solution, car nous devons dupliquer le même code pour chaque entité résolvable.


Mais tous ces problèmes peuvent être résolus si la fonctionnalité associée à une propriété insignifiante est supprimée en tant que mélange distinct (mixin):


 enum Color {red, yellow, green} mixin PaintableMixin { Color _color; void paint(Color color) {_color = color;} Color get color => _color; } class PaintableRect extends Rectangle with PaintableMixin { @override void draw() { print('Draw rectangle with color $color'); } } class PaintableContainer extends Container with PaintableMixin { @override void render() { print('Render container with color $color'); } } 

Vous pouvez maintenant l'utiliser:


 main() { PaintableRect() ..paint(Color.red) ..draw(); PaintableContainer() ..paint(Color.yellow) ..render(); } 

Pour résumer ce qui précède, on peut déterminer de la manière suivante quand il est commode d'utiliser des impuretés: s'il existe plusieurs hiérarchies différentes qui doivent ajouter la même fonction qui définit une propriété non essentielle pour les entités de ces hiérarchies. Ou il peut s'agir d'une seule hiérarchie, mais nous avons affaire à ses différentes branches. À titre d'exemple, considérons les widgets du framework Flutter.


Supposons que nous devions ajouter des fonctionnalités liées à la même propriété à certains widgets. Les widgets dans Flutter sont construits comme suit:


 class MyStatelessWidget extends StatelessWidget {} 

ou


 class MyStatefulWidget extends StatefulWidget {} 

Pour ajouter une propriété par héritage, vous devrez implémenter au moins deux classes:


 class StatelessWidgetWithProperty extends StatelessWidget {} class StatefulWidgetWithPropery extends StatefulWidget {} 

en même temps, comme vous pouvez le voir à nouveau, vous devez dupliquer la fonctionnalité associée à la propriété ajoutée.


Lors de l'utilisation d'impuretés, le problème est résolu:


 mixin Property {} class MyStatelessWidget extends StatelessWidget with Propery {} class MyStatefulWidget extends StatefulWidget with Property {} 

Pour ceux qui connaissent les modèles de conception, l'utilisation d'impuretés dans certains cas peut remplacer l'utilisation du modèle Bridge .


En conclusion, il convient de noter que de cette manière, on peut mélanger la fonctionnalité de plusieurs propriétés différentes à la fois dans des combinaisons arbitraires.


Cet article ne prétend pas définir de manière exhaustive l'utilisation des impuretés. L'esprit curieux du développeur sera probablement en mesure de leur trouver de bien plus belles utilisations. Je serais heureux si ces options d'utilisation des impuretés apparaissent dans les commentaires sur cet article.

Source: https://habr.com/ru/post/fr484206/


All Articles