Verwendung von Mixins in Dart

Mehrmals wurde von Kollegen die Frage gestellt, warum überhaupt Mixins (Verunreinigungen) in der Dart-Sprache benötigt werden. Ich habe mich dazu entschlossen zu sehen, was es zu diesem Thema im Internet gibt. Sehr zum Leidwesen der Artikel, die gefunden werden konnten, sprechen sie hauptsächlich über die Verwendung von Verunreinigungen, erklären jedoch nicht, warum sie benötigt werden. In diesen Fällen ist ihre Verwendung der gewöhnlichen Vererbung oder Implementierung von Schnittstellen vorzuziehen. Dieser Artikel ist ein Versuch, diese Lücke zu schließen.


Trotz der Tatsache, dass es im Internet genügend Artikel zum Thema Verunreinigungen in Dart und Flutter gibt, bringen sie meiner Meinung nach keine Klarheit, da die angegebenen Beispiele die reine Mechanik des Aufbaus von Klassen mit Verunreinigungen zeigen, was alles andere als vernünftig ist und daher nicht den tatsächlichen Anwendungsbereich aufzeigt . Insbesondere habe ich ein solches Beispiel getroffen . Wir haben:


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

Und aus irgendeinem Grund wollten wir ein Tier bekommen, das gleichzeitig die Eigenschaften von Katzen und Hunden hat. In diesem Fall können wir dies tun:


 class CatDog extends Animal with Cat, Dog {} 

Zu diesem Beispiel gibt es mindestens zwei Fragen:


  • Warum brauchen wir eine Kreuzung zwischen einer Katze und einem Hund?
  • Warum erben Katze und Hund nicht von Animal ? Sind sie keine Tiere?

Warum werden dennoch Verunreinigungen benötigt? Es bleibt ein Rätsel.


Meiner bescheidenen Meinung nach ist es zum Verständnis der Bedeutung von Verunreinigungen erforderlich, die Frage zunächst mit dem Verhältnis der Vererbung zu behandeln. Der Hauptvererbungspunkt in OOP ist, dass eine Entität eine Variation einer anderen Entität ist. Zum Beispiel ist das eine Variation der oder die ist eine Variation des . Und genau das sollte der entscheidende Faktor beim Aufbau einer Klassenhierarchie sein.


Wenn wir die Vererbung aus einem anderen Blickwinkel betrachten, werden wir feststellen, dass das die Eigenschaften der und die die Eigenschaften des erbt. Wenn Sie der Logik keine Aufmerksamkeit schenken, möchten Sie möglicherweise rein technisch die Eigenschaften mehrerer verschiedener Entitäten erben. Zu diesem Zweck unterstützen einige Programmiersprachen die Mehrfachvererbung .


Die mehrfache Vererbung wird wegen einer Reihe von Mängeln kritisiert (siehe Wikipedia ). Daher verwenden viele Programmiersprachen überhaupt keine mehrfache Vererbung, sondern einen Mechanismus zum Implementieren von Schnittstellen und / oder Verunreinigungen. Und aus logischer Sicht sind die Konstruktionen, die sich aus der Mehrfachvererbung ergeben, nicht leicht zu verstehen.


Um das folgende Material zu verstehen, müssen einige Konzepte aus der Elementarlogik abgerufen werden. Insbesondere die Konzepte der wesentlichen und unwesentlichen Eigenschaften. Die wesentlichen Eigenschaften von Objekten sind diejenigen, aufgrund deren Vorhandensein sie sich auf eine bestimmte Klasse von Objekten beziehen. Nicht wesentliche Eigenschaften eines Objekts sind diejenigen, deren Vorhandensein, Nichtvorhandensein oder bestimmte Werte das Objekt einer bestimmten Objektklasse nicht beeinflussen. Zum Beispiel ist die Form eines Rechtecks ​​eine wesentliche Eigenschaft dieser Form, denn wenn wir diese Form ändern (eine Seite entfernen oder hinzufügen oder die Winkel ändern), wird das Rechteck kein Rechteck mehr. Wenn Sie jedoch die Größe des Rechtecks ​​ändern, bleibt es weiterhin ein Rechteck. Abmessungen sind daher eine unbedeutende Eigenschaft.


Das Erstellen einer Klassenhierarchie basiert normalerweise auf dem Hinzufügen wesentlicher Eigenschaften zur übergeordneten Klasse. Zum Beispiel


 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'); } } 

Grundlage dieser Hierarchie ist die wesentliche Eigenschaft der Figurenform.


Ein weiteres Beispiel:


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

Eine wesentliche Eigenschaft ist hierbei der Zweck des Widgets.


Nehmen wir nun an, wir müssten unseren Entitäten einige nicht wesentliche Eigenschaften hinzufügen. Eine solche Eigenschaft ist beispielsweise Farbe. Lassen Sie uns nun einige Formen und Widgets kolorieren.


Dazu können Sie natürlich die Vererbung verwenden und zuerst die PaintableWidget PaintableShape und PaintableWidget implementieren. Dies ist jedoch nicht bequem, da wir erstens die Implementierung der Farbfunktionalität in beiden Hierarchien duplizieren müssen und zweitens für jede Figur und jedes Widget, die wir kolorieren möchten, neue Klassen implementieren müssen, z. B. PaintableRect und PaintableContainer .


Sie können den Mechanismus zum Implementieren von Schnittstellen verwenden. Dann bekommen wir so etwas:


 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; } 

Wie Sie sehen, ist dies auch nicht die beste Lösung, da für jede auflösbare Entität derselbe Code dupliziert werden muss.


All diese Probleme können jedoch gelöst werden, wenn die mit einer unbedeutenden Eigenschaft verbundene Funktion als separates Gemisch (Mixin) entfernt wird:


 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'); } } 

Jetzt können Sie es verwenden:


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

Zusammenfassend lässt sich auf folgende Weise feststellen, wann die Verwendung von Verunreinigungen zweckmäßig ist: Gibt es mehrere verschiedene Hierarchien, die dieselbe Funktion hinzufügen müssen, die einige nicht wesentliche Eigenschaften für die Entitäten dieser Hierarchien definiert. Oder es kann eine Hierarchie sein, aber wir haben es mit ihren verschiedenen Zweigen zu tun. Betrachten Sie als Beispiel die Widgets des Flutter-Frameworks.


Angenommen, wir müssen einigen Widgets Funktionen hinzufügen, die sich auf dieselbe Eigenschaft beziehen. Widgets in Flutter sind wie folgt aufgebaut:


 class MyStatelessWidget extends StatelessWidget {} 

oder


 class MyStatefulWidget extends StatefulWidget {} 

Um eine Eigenschaft durch Vererbung hinzuzufügen, müssen Sie mindestens zwei Klassen implementieren:


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

Gleichzeitig müssen Sie, wie Sie erneut sehen, die mit der hinzugefügten Eigenschaft verknüpften Funktionen duplizieren.


Bei Verwendung von Verunreinigungen ist das Problem gelöst:


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

Für diejenigen, die mit Entwurfsmustern vertraut sind, kann die Verwendung von Verunreinigungen in einigen Fällen die Verwendung des Brückenmusters ersetzen.


Zusammenfassend ist festzuhalten, dass man auf diese Weise in beliebiger Kombination mehrere unterschiedliche Eigenschaften gleichzeitig in die Funktion einmischen kann.


Dieser Artikel soll die Verwendung von Verunreinigungen nicht erschöpfend definieren. Wahrscheinlich wird der forschende Verstand des Entwicklers in der Lage sein, viel mehr schöne Verwendungsmöglichkeiten für sie zu finden. Ich würde mich freuen, wenn diese Optionen zur Verwendung von Verunreinigungen in den Kommentaren zu diesem Artikel erscheinen.

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


All Articles