Patrones y coincidencia (3.0)
Parte de la sección Programación Orientada a Objetos del Journey de Dart de Coddy — lección 87 de 110.
Dart 3 introdujo la coincidencia de patrones (pattern matching), una forma poderosa de desestructurar e inspeccionar datos en una sola expresión. Los patrones permiten extraer valores de objetos, comprobar su estructura y vincular variables, todo a la vez.
Ya has visto la desestructuración básica con registros. Los patrones extienden esto para trabajar con objetos, listas y mapas:
void main() {
// Patrones de lista
var numbers = [1, 2, 3];
var [a, b, c] = numbers;
print('$a, $b, $c'); // 1, 2, 3
// Patrones de mapa
var user = {'name': 'Alice', 'age': 30};
var {'name': name, 'age': age} = user;
print('$name is $age'); // Alice is 30
}Los patrones se vuelven especialmente potentes en las expresiones switch. Puedes realizar coincidencias por tipo, valor y estructura simultáneamente:
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)); // Número negativo
print(describe(42)); // Número positivo: 42
print(describe([1, 2, 3])); // Lista que comienza con 1
}La cláusula when añade condiciones a los patrones, y ... coincide con los elementos restantes en una lista. El guion bajo _ actúa como un comodín que coincide con cualquier cosa. Combinados con clases selladas, los patrones permiten un manejo exhaustivo y de tipo seguro de estructuras de datos complejas.
Desafío
Fácil¡Vamos a construir un clasificador de datos que utilice el emparejamiento de patrones (pattern matching) para analizar y categorizar diferentes tipos de entrada! Crearás un sistema que pueda identificar y describir varias estructuras de datos utilizando las potentes funciones de pattern matching de Dart 3.
Organizarás tu código en dos archivos:
classifier.dart: Crea una función llamadaclassifyque reciba un parámetro de tipoObjecty devuelva una descripción de tipoString. Utiliza una expresión switch con pattern matching para manejar estos casos:- Una lista vacía
[]debe devolver'Empty list' - Una lista con exactamente un elemento entero debe devolver
'Single item: $value' - Una lista con exactamente dos elementos debe devolver
'Pair: $first and $second' - Una lista que comience con un entero seguido de más elementos debe devolver
'List starting with $first, length: ${rest.length + 1}'(utiliza el patrón de resto...) - Un mapa que contenga las claves
'name'y'age'debe devolver'$name is $age years old'(utiliza la desestructuración de patrones de mapa) - Un entero que sea negativo (utiliza la cláusula
when) debe devolver'Negative: $n' - Un entero que sea cero debe devolver
'Zero' - Cualquier otro entero debe devolver
'Positive: $n' - Cualquier otro valor debe devolver
'Unknown type'
- Una lista vacía
main.dart: Importa tu clasificador y demuestra el pattern matching con varias entradas. Imprime el resultado de clasificar cada uno de estos valores:- Una lista vacía
[] - Una lista con un elemento:
[42] - Una lista con dos elementos:
['hello', 'world'] - Una lista más larga:
[1, 2, 3, 4, 5] - Un mapa:
{'name': 'Alice', 'age': 25} - El entero
-10 - El entero
0 - El entero
99
- Una lista vacía
¡Este desafío combina patrones de lista, patrones de mapa, la cláusula when para guardas y el patrón de resto (...), todas las características clave de pattern matching de la lección!
Salida esperada:
Empty list
Single item: 42
Pair: hello and world
List starting with 1, length: 5
Alice is 25 years old
Negative: -10
Zero
Positive: 99Hoja de referencia
Dart 3 introdujo el pattern matching (coincidencia de patrones) para la desestructuración e inspección de datos en una sola expresión.
Patrones de Listas
Extrae valores de listas:
var numbers = [1, 2, 3];
var [a, b, c] = numbers;
print('$a, $b, $c'); // 1, 2, 3Patrones de Mapas
Desestructura mapas por clave:
var user = {'name': 'Alice', 'age': 30};
var {'name': name, 'age': age} = user;
print('$name is $age'); // Alice is 30Expresiones Switch con Patrones
Coincidencia por tipo, valor y estructura:
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',
};
}Características de los Patrones
- Cláusula
when: Añade condiciones a los patrones ...(patrón de resto): Coincide con los elementos restantes en una lista_(comodín): Coincide con cualquier cosa
Pruébalo tú mismo
import 'classifier.dart';
void main() {
// TODO: Usa la función classify para categorizar cada valor
// y mostrar los resultados
// Prueba con una lista vacía
print(classify([]));
// TODO: Prueba con una lista que contenga un elemento: [42]
// TODO: Prueba con una lista que contenga dos elementos: ['hello', 'world']
// TODO: Prueba con una lista más larga: [1, 2, 3, 4, 5]
// TODO: Prueba con un map: {'name': 'Alice', 'age': 25}
// TODO: Prueba con el entero -10
// TODO: Prueba con el entero 0
// TODO: Prueba con el entero 99
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
1Fundamentos de POO
Archivos externosLibrerías e importacionesIntroducción a la POOClases vs. ObjetosLa palabra clave thisMétodosVariables de instanciaConceptos básicos de constructoresResumen - Calculadora sencilla4Seguridad de nulos
Introducción a la seguridad de nulosNulables vs No nulablesLos operadores ? y !Palabra clave Late y Null SafetyOperadores Null-awareNull Safety en clasesResumen - Sistema de perfil de usuario7Clases abstractas e interfaces
Clases abstractasMétodos abstractosInterfaces en DartInterfaces implícitasImplementar vs. ExtenderInterfaces múltiplesRepaso - Calculadora de figuras10Colecciones y Genéricos
Visión general de List, Set y MapColecciones con seguridad de tiposClases genéricasMétodos genéricosRestricciones genéricasIterable e IteratorResumen - Almacenamiento genérico13Conceptos avanzados de POO
Composición vs. HerenciaMétodos de extensiónClases invocablesClases selladas (Dart 3)Records (Dart 3)Patrones y coincidencia (3.0)Enums con métodos2Constructores en Dart
Constructor por defectoConstructores nombradosListas de inicializaciónConstructores constantesConstructores de fábricaConstructores de redireccionamientoResumen - Constructor de formas5Encapsulamiento
Miembros públicos vs. privadosLa convención del prefijo _Privacidad a nivel de libreríaProfundidad en Getters y SettersOcultamiento de informaciónResumen - Registros de estudiantes8Mixins
Introducción a los MixinsCreación de MixinsUso de múltiples MixinsPalabra clave on en MixinsMixin vs HerenciaMixin vs InterfazResumen - Sistema Animal11Métodos especiales
Sobrescritura de toString()Sobrescritura de hashCode y ==Interfaz ComparableMétodo call()Sobrescritura de noSuchMethodResumen - Colección personalizada14Patrones de diseño Parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón ObserverPatrón Strategy3Propiedades de clase
Miembros de instancia vs. estáticosCampos Final y ConstVariables LateMétodos y campos estáticosGetters y SettersResumen - Gestor de cuentas bancarias6Herencia
Herencia básicaLa palabra clave superSobrescritura de métodosLa anotación @overrideLa palabra clave final en clasesConstructores y herenciaResumen - Jerarquía de empleados9Polimorfismo
Fundamentos del polimorfismoPolimorfismo mediante interfacesComprobación de tipos en tiempo de ejecuciónLos operadores is y asPalabra clave covariantResumen - Procesador de pagos12POO Asíncrona
Futures y async/awaitFundamentos de StreamsStream ControllersConstructores asíncronosAsincronía en métodos de claseRepaso - Data Fetcher15Patrones de diseño Parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón CompositePatrón Repository