Паттерны и сопоставление (3.0)
Часть раздела Объектно-ориентированное программирование путешествия по Dart на Coddy — урок 87 из 110.
В Dart 3 появилось сопоставление с образцом — мощный способ деструктуризации и проверки данных в одном выражении. Шаблоны позволяют извлекать значения из объектов, проверять их структуру и привязывать переменные — и всё это одновременно.
Вы уже видели базовую деструктуризацию с записями. Шаблоны расширяют это для работы с объектами, списками и картами:
void main() {
// Шаблоны списков
var numbers = [1, 2, 3];
var [a, b, c] = numbers;
print('$a, $b, $c'); // 1, 2, 3
// Шаблоны карт
var user = {'name': 'Alice', 'age': 30};
var {'name': name, 'age': age} = user;
print('$name is $age'); // Alice is 30
}Шаблоны становятся особенно мощными в выражениях switch. Вы можете сопоставлять по типу, значению и структуре одновременно:
String describe(Object obj) {
return switch (obj) {
int n when n < 0 => 'Negative number',
int n => 'Positive number: $n',
String s => 'String of length ${s.length}',
[int first, ...] => 'List starting with $first',
_ => 'Something else',
};
}
void main() {
print(describe(-5)); // Отрицательное число
print(describe(42)); // Положительное число: 42
print(describe([1, 2, 3])); // Список, начинающийся с 1
}Конструкция when добавляет условия к шаблонам, а ... соответствует оставшимся элементам в списке. Символ подчеркивания _ служит подстановочным знаком, который соответствует чему угодно. В сочетании с изолированными (sealed) классами шаблоны обеспечивают исчерпывающую и типобезопасную обработку сложных структур данных.
Задание
ЛегкоДавайте создадим классификатор данных, который использует сопоставление с образцом для анализа и категоризации различных типов входных данных! Вы создадите систему, которая может идентифицировать и описывать различные структуры данных, используя мощные возможности сопоставления с образцом в Dart 3.
Вы организуете свой код в два файла:
classifier.dart: Создайте функцию с именемclassify, которая принимает параметрObjectи возвращает описание типаString. Используйте выражение switch с сопоставлением с образцом для обработки следующих случаев:- Пустой список
[]должен возвращатьEmpty list - Список, содержащий ровно один целочисленный элемент, должен возвращать
Single item: [value] - Список, содержащий ровно два элемента, должен возвращать
Pair: [first] and [second] - Список, начинающийся с целого числа, за которым следуют другие элементы, должен возвращать
List starting with [first], length: [total length](используйте rest-паттерн...) - Map, содержащий ключи
'name'и'age', должен возвращать[name] is [age] years old(используйте деструктуризацию паттерна map) - Целое число, которое является отрицательным (используйте предложение
when), должно возвращатьNegative: [value] - Целое число, равное нулю, должно возвращать
Zero - Любое другое целое число должно возвращать
Positive: [value] - Любое другое значение должно возвращать
Unknown type
- Пустой список
main.dart: Импортируйте свой классификатор и продемонстрируйте сопоставление с образцом на различных входных данных. Выведите результат классификации каждого из этих значений:- Пустой список
[] - Список с одним элементом:
[42] - Список с двумя элементами:
['hello', 'world'] - Более длинный список:
[1, 2, 3, 4, 5] - Map:
{'name': 'Alice', 'age': 25} - Целое число
-10 - Целое число
0 - Целое число
99
- Пустой список
Эта задача объединяет паттерны списков, паттерны map, предложение when для условий (guards) и rest-паттерн (...) — все ключевые особенности сопоставления с образцом из урока!
Ожидаемый вывод:
Empty list
Single item: 42
Pair: hello and world
List starting with 1, length: 5
Alice is 25 years old
Negative: -10
Zero
Positive: 99Шпаргалка
В Dart 3 появилось сопоставление с образцом (pattern matching) для деструктуризации и проверки данных в одном выражении.
Образцы списков
Извлечение значений из списков:
var numbers = [1, 2, 3];
var [a, b, c] = numbers;
print('$a, $b, $c'); // 1, 2, 3Образцы карт (Map)
Деструктуризация карт по ключу:
var user = {'name': 'Alice', 'age': 30};
var {'name': name, 'age': age} = user;
print('$name is $age'); // Alice is 30Выражения switch с образцами
Сопоставление по типу, значению и структуре:
String describe(Object obj) {
return switch (obj) {
int n when n < 0 => 'Negative number',
int n => 'Positive number: $n',
String s => 'String of length ${s.length}',
[int first, ...] => 'List starting with $first',
_ => 'Something else',
};
}Особенности образцов
- Предложение
when: добавляет условия к образцам ...(остаточный образец): соответствует оставшимся элементам в списке_(подстановочный знак): соответствует чему угодно
Попробуйте сами
import 'classifier.dart';
void main() {
// TODO: Используйте функцию classify для категоризации каждого значения
// и выведите результаты
// Тест с пустым списком
print(classify([]));
// TODO: Протестируйте со списком, содержащим один элемент: [42]
// TODO: Протестируйте со списком, содержащим два элемента: ['hello', 'world']
// TODO: Протестируйте с более длинным списком: [1, 2, 3, 4, 5]
// TODO: Протестируйте с картой (map): {'name': 'Alice', 'age': 25}
// TODO: Протестируйте с целым числом -10
// TODO: Протестируйте с целым числом 0
// TODO: Протестируйте с целым числом 99
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыБиблиотеки и импортВведение в ООПКлассы и объектыКлючевое слово thisМетодыПеременные экземпляраОсновы конструкторовИтоги — Простой калькулятор4Null-безопасность
Введение в Null-безопасностьNullable и Non-Nullable типыОператоры ? и !Ключевое слово Late и Null-безопасностьNull-aware операторыNull-безопасность в классахИтоги — Система профилей пользователей7Абстрактные классы и интерфейсы
Абстрактные классыАбстрактные методыИнтерфейсы в DartНеявные интерфейсыРеализация против наследованияМножественные интерфейсыИтоги — Калькулятор фигур10Коллекции и обобщения
Обзор List, Set, MapТипобезопасные коллекцииОбобщенные классыОбобщенные методыОграничения обобщенийIterable и IteratorИтоги: Обобщенное хранилище13Продвинутые концепции ООП
Композиция против наследованияМетоды расширенияВызываемые классыЗапечатанные классы (Dart 3)Записи (Dart 3)Паттерны и сопоставление (3.0)Перечисления с методами16Проект: Управление библиотекой
Обзор проектаКлассы Book и User2Конструкторы в Dart
Конструктор по умолчаниюИменованные конструкторыСписки инициализацииКонстантные конструкторыФабричные конструкторыПеренаправляющие конструкторыИтоги — Shape Builder5Инкапсуляция
Публичные и приватные членыСоглашение о префиксе _Приватность на уровне библиотекГеттеры и сеттеры: подробный разборСкрытие данныхИтоги — Записи о студентах8Mixins
Введение в MixinsСоздание MixinsИспользование нескольких MixinsКлючевое слово on в MixinsMixin и наследованиеMixin и интерфейсИтоги: Система животных11Специальные методы
Переопределение toString()Переопределение hashCode и ==Интерфейс ComparableМетод call()Переопределение noSuchMethodПовторение — Пользовательская коллекция14Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн ObserverПаттерн Strategy3Свойства классов
Члены экземпляра и статические членыПоля Final и ConstПеременные LateСтатические методы и поляГеттеры и сеттерыИтоги — Менеджер банковского счета6Наследование
Основы наследованияКлючевое слово superПереопределение методовАннотация @overrideКлючевое слово final для классовКонструкторы и наследованиеИтоги: Иерархия сотрудников9Полиморфизм
Основы полиморфизмаПолиморфизм через интерфейсыПроверка типов во время выполненияОператоры is и asКлючевое слово covariantИтоги — Обработчик платежей12Асинхронное ООП
Futures и async/awaitОсновы StreamsКонтроллеры StreamАсинхронные конструкторыАсинхронность в методах классовИтоги — Data Fetcher15Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикПаттерн Репозиторий