|
| 1 | +--- |
| 2 | +title: Dart features |
| 3 | +description: Smaczki Dart, które warto znać |
| 4 | +--- |
| 5 | + |
| 6 | +## Mechanizmy Darta, które warto znać |
| 7 | + |
| 8 | +Dart jest językiem wieloparadygmatowym — pozwala pisać zarówno w stylu imperatywnym, jak i funkcyjnym. Ma wiele przydatnych mechanizmów, o których łatwo zapomnieć, a które mogą ułatwić pisanie prostszego i bardziej niezawodnego kodu. Oto krótkie podsumowanie niektórych ficzerów, o których warto pamiętać. |
| 9 | + |
| 10 | +## New Switch-Case |
| 11 | + |
| 12 | +Dla Darta od `3.0` w górę dostępna jest nowa wersja switch-case'a, która działa jako wyrażenie i wspiera `pattern matching` oraz `guard clauses`. W dokumentacji Dart'a można znaleźć starą składnię switch'a pod nazwą [`switch statement`](https://arc.net/l/quote/ltljrcla) a nową jako [`switch expression`](https://arc.net/l/quote/ofmreksm). |
| 13 | + |
| 14 | +### Porównanie: switch statement |
| 15 | + |
| 16 | +Widzimy tu ciąg imperatywnych instrukcji, który zostanie wykonany, gdy warunek opisany w `case` zostanie spełniony, np.: |
| 17 | + |
| 18 | +```dart |
| 19 | +var command = 'OPEN'; |
| 20 | +switch (command) { |
| 21 | + case 'CLOSED': |
| 22 | + executeClosed(); |
| 23 | + case 'PENDING': |
| 24 | + executePending(); |
| 25 | + case 'APPROVED': |
| 26 | + executeApproved(); |
| 27 | + case 'DENIED': |
| 28 | + executeDenied(); |
| 29 | + case 'OPEN': |
| 30 | + executeOpen(); |
| 31 | + default: |
| 32 | + executeUnknown(); |
| 33 | +} |
| 34 | +``` |
| 35 | + |
| 36 | +Ale co jeśli w wyniku switcha chcemy po prostu przypisać konkretną wartość do danej zmiennej? Korzystając ze `switch statement`, będzie wyglądać to tak: |
| 37 | + |
| 38 | +```dart |
| 39 | +// Where slash, star, comma, semicolon, etc., are constant variables... |
| 40 | +switch (charCode) { |
| 41 | + case slash || star || plus || minus: // Logical-or pattern |
| 42 | + token = operator(charCode); |
| 43 | + case comma || semicolon: // Logical-or pattern |
| 44 | + token = punctuation(charCode); |
| 45 | + case >= digit0 && <= digit9: // Relational and logical-and patterns |
| 46 | + token = number(); |
| 47 | + default: |
| 48 | + throw FormatException('Invalid'); |
| 49 | +} |
| 50 | +``` |
| 51 | + |
| 52 | +De facto, jedyne co robimy za każdym razem, to przypisujemy zmiennej wartość, możnaby więc wykonać to szybciej - z pomocą przychodzi `switch expression`. |
| 53 | + |
| 54 | +### Porównanie: switch expression |
| 55 | + |
| 56 | +Tutaj zaznaczamy, że do zmiennej `token` zostanie przypisane cokolwiek zwróci nam _wyrażenie_ switch, które sprowadza się do: |
| 57 | +\[dopasowanie\] => \[zwracana wartość\]. |
| 58 | + |
| 59 | +```dart |
| 60 | + final token = switch (charCode) { |
| 61 | + slash || star || plus || minus => operator(charCode), |
| 62 | + comma || semicolon => punctuation(charCode), |
| 63 | + >= digit0 && <= digit9 => number(), |
| 64 | + _ => throw FormatException('Invalid'), |
| 65 | +}; |
| 66 | +``` |
| 67 | + |
| 68 | +Krótsze, a przede wszystkim nie wymusza konieczności ręcznego przypisywania wartości do zmiennej pod każdym `case`. |
| 69 | + |
| 70 | +## Pattern Matching |
| 71 | + |
| 72 | +[`Pattern matching`](https://dart.dev/language/patterns) w Darcie to potężny mechanizm, który pozwala na dopasowywanie wartości według ich struktury, typu czy zawartości. Wprowadzony w Darcie `3.0`, otwiera nowe możliwości pisania czytelnego i bezpiecznego kodu. Oprócz dopasowywania wartości wewnątrz `switch statement` i `switch expression`, pozwala też na `destructuring` - wyciągnięcie konkretnych pól/atrybutów ze złożonych struktur. |
| 73 | + |
| 74 | +### Zalety wykorzystywania pattern matchingu |
| 75 | + |
| 76 | +- Czytelność - kod staje się bardziej deklaratywny i łatwiejszy do zrozumienia. |
| 77 | +- Bezpieczeństwo typów - kompilator sprawdza kompletność wzorców. |
| 78 | +- Mniej boilerplate - eliminuje potrzebę wielu if-else lub instanceof checks. |
| 79 | +- Destructuring - łatwe wyciąganie wartości ze złożonych struktur danych. |
| 80 | +- Kompozycja wzorców - możliwość łączenia różnych typów wzorców. |
| 81 | + |
| 82 | +### Wykorzystanie: Pattern matching w switch statement |
| 83 | + |
| 84 | +`Switch statement` wykonuje różne instrukcje w zależności od dopasowanego typu danych: |
| 85 | + |
| 86 | +```dart |
| 87 | +switch (value) { |
| 88 | + case int i when i > 0: |
| 89 | + print('Positive number'); |
| 90 | + break; |
| 91 | + case String s: |
| 92 | + print('This is a string: $s'); |
| 93 | + break; |
| 94 | + default: |
| 95 | + print('Other type'); |
| 96 | +} |
| 97 | +``` |
| 98 | + |
| 99 | +### Wykorzystanie: Pattern matching w switch expression |
| 100 | + |
| 101 | +`Switch expression` zwraca różne wartości w zależności od dopasowanego typu danych: |
| 102 | + |
| 103 | +```dart |
| 104 | +String message = switch (value) { |
| 105 | + int i when i > 0 => 'Positive number', |
| 106 | + String s => 'This is a string: $s', |
| 107 | + _ => 'Other type', |
| 108 | +}; |
| 109 | +``` |
| 110 | + |
| 111 | +### Wykorzystanie: Pattern matching w destructuringu |
| 112 | + |
| 113 | +`Pattern matching` można wykorzystać aby wyciągnąć konkretne dane ze struktur, np. z list, tupli, map: |
| 114 | + |
| 115 | +```dart |
| 116 | +final numList = [1, 2, 3]; |
| 117 | +// List pattern [a, b, c] destructures the three elements from numList... |
| 118 | +final [a, b, c] = numList; |
| 119 | +// ...and assigns them to new variables. |
| 120 | +print(a + b + c); |
| 121 | +``` |
| 122 | + |
| 123 | +### Wykorzystanie: Użycie wieloznacznika - wildcard |
| 124 | + |
| 125 | +`Wildcard` lub też wieloznacznik oznaczany jest jako `_` i może pełnić podobną funkcję w `switch expression` co `default` w `switch statement`, np.: |
| 126 | + |
| 127 | +```dart |
| 128 | +String stringOrInt(Object value) => switch (value) { |
| 129 | + String s => 'This is a string.', |
| 130 | + int i => 'This is an int.', |
| 131 | + _ => 'Neither string nor int.', |
| 132 | +}; |
| 133 | +``` |
| 134 | + |
| 135 | +Dodatkowo, przy destructuringu może oznaczać te elementy, które nas nie interesują, np.: |
| 136 | + |
| 137 | +```dart |
| 138 | +final importantList = ['boring', 'important', 'something', 'something more' ]; |
| 139 | +final [_, i, _, _] = importantList; |
| 140 | +print('This one is important: $i'); |
| 141 | +``` |
| 142 | + |
| 143 | +`Wildcard` może być również stosowany wtedy, gdy interesuje nas typ zmiennej, ale nie chcemy jej później wykorzystywać, np.: |
| 144 | + |
| 145 | +```dart |
| 146 | +switch (record) { |
| 147 | + case (int _, String _): |
| 148 | + print('First field is int and second is String.'); |
| 149 | +} |
| 150 | +``` |
| 151 | + |
| 152 | +### Rodzaje wzorców |
| 153 | + |
| 154 | +#### Constant patterns |
| 155 | + |
| 156 | +Dopasowują dokładną wartość: |
| 157 | + |
| 158 | +```dart |
| 159 | +String describe(int value) => switch (value) { |
| 160 | + 0 => 'zero', |
| 161 | + 1 => 'one', |
| 162 | + 42 => 'the answer', |
| 163 | + _ => 'some number', |
| 164 | +}; |
| 165 | +``` |
| 166 | + |
| 167 | +#### Variable patterns |
| 168 | + |
| 169 | +Wiążą dopasowaną wartość z nową zmienną: |
| 170 | + |
| 171 | +```dart |
| 172 | +String processValue(Object value) => switch (value) { |
| 173 | + int x when x > 0 => 'positive: $x', |
| 174 | + int x when x < 0 => 'negative: $x', |
| 175 | + int x => 'zero: $x', |
| 176 | + String s => 'text: $s', |
| 177 | + _ => 'unknown type', |
| 178 | +}; |
| 179 | +``` |
| 180 | + |
| 181 | +#### Record patterns |
| 182 | + |
| 183 | +Dopasowują rekordy według struktury: |
| 184 | + |
| 185 | +```dart |
| 186 | +String analyzePoint((int, int) point) => switch (point) { |
| 187 | + (0, 0) => 'origin', |
| 188 | + (final x, 0) => 'on x-axis at $x', |
| 189 | + (0, final y) => 'on y-axis at $y', |
| 190 | + (var x, var y) when x == y => 'diagonal at ($x, $y)', |
| 191 | + (var x, var y) => 'point at ($x, $y)', |
| 192 | +}; |
| 193 | +``` |
| 194 | + |
| 195 | +:::tip |
| 196 | +Jeśli nie podajemy typu zmiennej, to do jej opisania można użyć zarówno final jak i var, w zależności od tego, czy planujemy ją modyfikować. |
| 197 | +::: |
| 198 | + |
| 199 | +#### List patterns |
| 200 | + |
| 201 | +Dopasowują listy według zawartości: |
| 202 | + |
| 203 | +```dart |
| 204 | +String analyzeList(List<int> numbers) => switch (numbers) { |
| 205 | + [] => 'empty list', |
| 206 | + [final single] => 'single element: $single', |
| 207 | + [final first, final second] => 'two elements: $first, $second', |
| 208 | + [final first, ...final rest] => 'starts with $first, has ${rest.length} more', |
| 209 | +}; |
| 210 | +``` |
| 211 | + |
| 212 | +#### Map patterns |
| 213 | + |
| 214 | +Dopasowują mapy według kluczy i wartości: |
| 215 | + |
| 216 | +```dart |
| 217 | +String analyzeUser(Map<String, dynamic> user) => switch (user) { |
| 218 | + {'name': String name, 'age': int age} when age >= 18 => 'Adult: $name ($age)', |
| 219 | + {'name': String name, 'age': int age} => 'Minor: $name ($age)', |
| 220 | + {'name': String name} => 'Name only: $name', |
| 221 | + _ => 'Invalid user data', |
| 222 | +}; |
| 223 | +``` |
| 224 | + |
| 225 | +#### Object patterns |
| 226 | + |
| 227 | +Dopasowują obiekty według ich właściwości: |
| 228 | + |
| 229 | +```dart |
| 230 | +sealed class Shape {} |
| 231 | +class Circle extends Shape { |
| 232 | + final double radius; |
| 233 | + Circle(this.radius); |
| 234 | +} |
| 235 | +class Rectangle extends Shape { |
| 236 | + final double width, height; |
| 237 | + Rectangle(this.width, this.height); |
| 238 | +} |
| 239 | +
|
| 240 | +double calculateArea(Shape shape) => switch (shape) { |
| 241 | + Circle(radius: final r) => 3.14159 * r * r, |
| 242 | + Rectangle(width: final w, height: final h) => w * h, |
| 243 | +}; |
| 244 | +``` |
| 245 | + |
| 246 | +Jeśli nie ma potrzeby zmieniać nazw pól obiektu w ramach dopasowania, można użyć także takiej notacji: |
| 247 | + |
| 248 | +```dart |
| 249 | +double calculateArea(Shape shape) => switch (shape) { |
| 250 | + Circle(:final radius) => 3.14159 * radius * radius, |
| 251 | + Rectangle(:final width, :final height) => width * height, |
| 252 | +}; |
| 253 | +
|
| 254 | +``` |
0 commit comments