Pattern-basierter Datenabgleich

Patterns können erst ab Dart-Version ≥ 3.0 verwendet werden

I. Kernaufgaben von Pattern

Pattern ist eine eigenständige Syntaxkategorie in Dart (gleichrangig mit Anweisungen und Ausdrücken), die die Struktur von Werten beschreibt. Es gibt zwei zentrale Funktionen, die gleichzeitig nutzbar sind: Abgleich (Match) und Dekonstruktion (Destructure).

  1. Abgleich Match:Prüfen, ob ein Wert der erwarteten Struktur entspricht
    • Ist es eine festgelegte Konstante?
    • Passt es zu einem bestimmten Typ?
    • Hat es eine definierte Struktur (Liste/Objekt/Map/Record)?
    • Erfüllt es eine Zusatzbedingung?
  2. Dekonstruktion Destructure:Bei erfolgreichem Abgleich werden interne Felder und Elemente schnell extrahiert und an Variablen gebunden

1. Beispiel für Konstantenabgleich

void main() {
	int number = 1;
	switch (number) {
	  case 1:
		print('one'); // Konstanten-Pattern prüft number == 1
	  default:
		print('anderer Wert');
	}
}Code-Sprache: Dart (dart)

Ausgabeergebnis

one

2. Geschachtelter Unterpattern-Abgleich

Patterns unterstützen verschachtelte Unterpattern, um interne Strukturen schrittweise zu prüfen

void main() {
  const a = 'a';
  const b = 'b';
  var obj = [ 'a', 'b'];
  switch (obj) {
    case [a, b]:
      print('$a, $b');
    default:
      print('Kein Treffer');
  }
}Code-Sprache: Dart (dart)

Ausgabeergebnis

a, b

3. Einfaches Dekonstruktionsbeispiel

var numList = [1, 2, 3];
// List-Pattern dekonstruiert Listenelemente und bindet an neue Variablen
var [x, y, z] = numList; 
print(x + y + z); // 6 x = 1, y = 2, z = 3Code-Sprache: PHP (php)

4. Verschachtelter Pattern-Abgleich + Dekonstruktion

void main() {
  var list = ['b', 99];
  switch (list) {
    // Logisches ODER-Pattern: Erstes Element ist a oder b, zweites an Variable c binden
    case ['a' || 'b', var c]:
      print(c); // 99
  }
}Code-Sprache: Dart (dart)

II. Häufig genutzte Pattern-Beispiele

1. Lokale Variablendeklaration

Zur dekonstruierenden Zuweisung, erstellt mehrere Variablen auf einmal

// Dekonstruktion mit verschachteltem Record + List-Pattern
var (str, [n1, n2]) = ('text', [10, 20]);
print(str); //text
print(n1 + n2); //30Code-Sprache: Dart (dart)

2. Neuzuweisung vorhandener Variablen (Wertaustausch ohne Hilfsvariable)

var (a, b) = ("links", "rechts");
(b, a) = (a, b); // Werte zweier Variablen tauschen
print('$a $b'); // rechts linksCode-Sprache: Dart (dart)

3. switch /if-case Verzweigungen (häufigster Einsatz, when-Wächterklausel)

Jedes case enthält widerlegbare Patterns: Bei fehlendem Abgleich wird das nächste case geprüft

Einfaches Mehrtyp-Case

void testSwitch(dynamic obj) {
  switch (obj) {
    case 1:
      print('Zahl 1');
    case >= 10 && <= 20:
      print('Zahl zwischen 10 und 20');
    case (var a, var b):
      print('Record: $a,$b');
    default:
      print('Kein passender Fall');
  }
}Code-Sprache: Dart (dart)

Logisches ODER-Pattern: Gemeinsame Logik für mehrere Fälle

enum Color { red, yellow, blue, green }
void main() {
  Color color = Color.red;
  bool isPrimary = switch (color) {
    Color.red || Color.yellow || Color.blue => true,
    _ => false,
  };
  print(isPrimary); // true
}
Code-Sprache: PHP (php)

when-Wächterklausel (Zusatzfilter nach erfolgreichem Patternabgleich)

sealed class Shape {}
class Square { final double size; Square(this.size); }
class Circle { final double size; Circle(this.size); }

void checkShape(Shape shape) {
  switch (shape) {
    // Gleichzeitiger Abgleich von Square und Circle mit Zusatzbedingung size>0
    case Square(size: var s) || Circle(size: var s) when s > 0:
      print('Gültige symmetrische Figur');
    default:
      print('Ungültige Figur');
  }
}Code-Sprache: Dart (dart)

Unterschied zwischen when und normalem if

void compareWhen((int, int) pair) {
  switch (pair) {
    case (int a, int b):
      if (a > b) print('Erster Wert größer');
      // Bei fehlender if-Bedingung wird der gesamte switch abgebrochen, nachfolgende cases laufen nicht
    case (int a, int b) when a > b:
      print('【when-Wächter】Erster Wert größer');
      // Bei fehlender when-Bedingung wird nur dieses case übersprungen, nächstes case wird geprüft
    case (int a, int b):
      print('Erster Wert kleiner oder gleich');
  }
}

void main() {
  print("===== Test (5, 2) a > b =====");
  compareWhen((5, 2));

  print("\n===== Test (2, 5) a < b =====");
  compareWhen((2, 5));
}Code-Sprache: Dart (dart)

4. for /for-in Schleifen (Dekonstruktion von MapEntry besonders praktisch)


void main() {
	Map<String, int> hist = {'a': 23, 'b': 100};
	
	// Volle Schreibweise
	for (var MapEntry(key: key, value: count) in hist.entries) {
	  print('$key : $count');
	}
	
	// Kurzschreibweise: key:key wird zu :key vereinfacht
	for (var MapEntry(:key, value: count) in hist.entries) {
	  print('$key : $count');
	}
}Code-Sprache: Dart (dart)

Ausgabeergebnis

D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
a : 23
b : 100
a : 23
b : 100
Code-Sprache: CSS (css)

III. Praktische Anwendungsfälle von Pattern

1: Mehrere Rückgabewerte einer Funktion (Record-Dekonstruktion)

Herkömmliche Schreibweise (umständlich)

(String, int) userInfo() => ("Lily", 13);
void main() {
  var info = userInfo();
  var name = info.$1;
  var age = info.$2;
}
Code-Sprache: Dart (dart)

Vereinfachte Dekonstruktion per Pattern

var (name, age) = userInfo();
// Dekonstruktion von Records mit benannten Feldern
final (:name, :age) = getData(); // Rückgabewert (name: "Doug", age:25)Code-Sprache: Dart (dart)

Konkretes Beispiel


(String, int) userInfo() => ("Lily", 18);


({String name, int age}) getData() => (name: "Doug", age: 25);

void main() {
  {
    var (name, age) = userInfo();
    print("Name: $name, Alter: $age");
  }

  // Interne name/age-Variablen kollidieren nicht mit den oberen
  {
    final (:name, :age) = getData();
    print("Name: $name, Alter: $age");
  }
}Code-Sprache: Dart (dart)

2: Dekonstruktion von Klasseninstanzen (Objekt-Pattern)

class Foo {
  final String one;
  final int two;
  Foo({required this.one, required this.two});
}

void main() {
  final Foo myFoo = Foo(one: "test", two: 66);
  var Foo(:one, :two) = myFoo;
  print('$one $two'); //test 66
}Code-Sprache: Dart (dart)

3: Algebraische Datentypen (Sealed-Klassen + Berechnung per switch-Verzweigung)

Gesammelte Verarbeitung zusammengehöriger Unterklassen ohne verstreute Instanzmethoden

import 'dart:math';

sealed class Shape {}
class Square implements Shape { final double length; Square(this.length); }
class Circle implements Shape { final double radius; Circle(this.radius); }

double calculateArea(Shape shape) => switch (shape) {
  Square(length: var l) => l * l,
  Circle(radius: var r) => pi * r * r,
};
Code-Sprache: Dart (dart)

Vollständiges Beispiel

import 'dart:math';

// Versiegelte Oberklasse, Unterklassen nur in dieser Datei definierbar
sealed class Shape {}

// Unterklasse Quadrat
class Square implements Shape {
  final double length;
  Square(this.length);
}

// Unterklasse Kreis
class Circle implements Shape {
  final double radius;
  Circle(this.radius);
}

// Einheitliche Flächenberechnung mit switch-Ausdruck + Objekt-Pattern
double calculateArea(Shape shape) => switch (shape) {
  // Treffer auf Square, extrahiere length in Variable l
  Square(length: var l) => l * l,
  // Treffer auf Circle, extrahiere radius in Variable r
  Circle(radius: var r) => pi * r * r,
};

void main() {
  // Figur-Instanzen erstellen
  Shape square = Square(5);
  Shape circle = Circle(3);

  // Flächen berechnen
  double squareArea = calculateArea(square);
  double circleArea = calculateArea(circle);

  print("Quadratfläche: $squareArea");    // 25.0
  print("Kreisfläche: ${circleArea.toStringAsFixed(2)}"); // 28.27
}Code-Sprache: Dart (dart)

Ausgabeergebnis

D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
Quadratfläche: 25.0
Kreisfläche: 28.27Code-Sprache: CSS (css)

4: JSON-Datenprüfung und Dekonstruktion (starke Vereinfachung verschachtelter Prüfungen)

(Ohne Pattern) Mehrere verschachtelte Typabfragen erzeugen langen Code

void oldCheck(Map data) {
  if (data is Map<String, Object?> && data.containsKey('user')) {
    var user = data['user'];
    if (user is List && user.length == 2) {
      String name = user[0] as String;
      int age = user[1] as int;
      print('$name $age');
    }
  }
}Code-Sprache: Dart (dart)

if-case-Pattern: Eine Zeile für Strukturprüfung + Typvalidierung + Variablenbindung

void newCheck(Map data) {
  if (data case {'user': [String name, int age]}) {
    print('Nutzer $name, Alter $age');
  }
}Code-Sprache: Dart (dart)

Dieses Pattern prüft gleichzeitig folgende Punkte:

  1. data ist eine Map und nicht null
  2. Der Schlüssel „user“ existiert
  3. Der Wert zu user ist eine Liste mit zwei Elementen
  4. Erstes Element String, zweites Element int
  5. Automatische Bindung der lokalen Variablen name und age

Praktisches Anwendungsbeispiel

void main() {
  // 1. Gültige JSON-Struktur: Treffer
  final validJson = {
    'user': ['Lily', 13]
  };
  newCheck(validJson); // Ausgabe: Nutzer Lily, Alter 13

  // 2. Typunverträglichkeit in Liste: Alter als String → kein Treffer
  final wrongTypeJson = {
    'user': ['Tom', '16']
  };
  newCheck(wrongTypeJson); // Keine Ausgabe

  // 3. Unvollständige user-Liste: Kein Alter → kein Treffer
  final shortJson = {
    'user': ['Jack']
  };
  newCheck(shortJson); // Keine Ausgabe

  // 4. Schlüssel „user“ fehlt → kein Treffer
  final noUserJson = {
    'info': ['Bob', 20]
  };
  newCheck(noUserJson); // Keine Ausgabe
}

// Einstufige Struktur-/Typ-Prüfung & Dekonstruktion mit Map+List-if-case-Pattern
void newCheck(Map data) {
  if (data case {'user': [String name, int age]}) {
    print('Nutzer $name, Alter $age');
  } else {
    print('Ungültiges Datenformat');
  }
}Code-Sprache: PHP (php)

Pattern-basierter Datenabgleich

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert