Menu
Coddy logo textTech

Clases selladas (Dart 3)

Parte de la sección Programación Orientada a Objetos del Journey de Dart de Coddy — lección 85 de 110.

Dart 3 introdujo las clases selladas, las cuales restringen qué clases pueden extenderlas o implementarlas. Cuando marcas una clase como sealed, solo las clases en el mismo archivo pueden ser sus subtipos directos. Esto crea un conjunto cerrado de subtipos conocidos.

sealed class Result {}

class Success extends Result {
  final String data;
  Success(this.data);
}

class Failure extends Result {
  final String error;
  Failure(this.error);
}

El poder de las clases selladas proviene de la comprobación de exhaustividad. Cuando realizas un switch sobre un tipo sellado, el compilador conoce todos los subtipos posibles y se asegura de que manejes cada uno de ellos:

String handleResult(Result result) {
  return switch (result) {
    Success(data: var d) => 'Got: $d',
    Failure(error: var e) => 'Error: $e',
  };
  // No se necesita un valor predeterminado: ¡el compilador sabe que todos los casos están cubiertos!
}

void main() {
  var success = Success('Hello');
  var failure = Failure('Not found');
  
  print(handleResult(success));  // Obtenido: Hello
  print(handleResult(failure));  // Error: Not found
}

Si añades un nuevo subtipo a una sealed class, el compilador marcará cada sentencia switch que no lo maneje. Esto hace que las sealed classes sean ideales para modelar estados, resultados o cualquier escenario donde tengas un conjunto fijo de variantes. A diferencia de las abstract classes, las sealed classes garantizan que ningún código externo pueda añadir subtipos inesperados.

challenge icon

Desafío

Fácil

¡Vamos a construir un manejador de peticiones de red utilizando clases selladas (sealed classes)! Crearás un sistema con seguridad de tipos que modela diferentes estados de una respuesta de red, asegurando que cada resultado posible se maneje adecuadamente.

Organizarás tu código en dos archivos:

  • response.dart: Crea una jerarquía de clases selladas que represente todos los estados posibles de una respuesta de red:
    • Una clase sellada llamada NetworkResponse que sirva como base para todos los tipos de respuesta
    • Una clase Loading que extienda NetworkResponse con un campo String message (por ejemplo, "Fetching data...")
    • Una clase Success que extienda NetworkResponse con un campo String data y un campo int statusCode
    • Una clase Error que extienda NetworkResponse con un campo String errorMessage y un campo int errorCode
  • main.dart: Importa tu archivo de respuesta y crea una función que maneje todos los tipos de respuesta utilizando una expresión switch:
    • Crea una función handleResponse(NetworkResponse response) que devuelva un String
    • Usa una expresión switch con coincidencia de patrones (pattern matching) para manejar cada caso:
      • Para Loading: devuelve Status: [message]
      • Para Success: devuelve Success ([statusCode]): [data]
      • Para Error: devuelve Error ([errorCode]): [errorMessage]
    • Crea tres instancias de respuesta e imprime el resultado de manejar cada una:
      • Un Loading con el mensaje Please wait...
      • Un Success con los datos User profile loaded y el código de estado 200
      • Un Error con el mensaje de error Not found y el código de error 404

La belleza de las clases selladas es que el compilador asegura que manejes cada tipo de respuesta posible: ¡no se necesita un caso por defecto!

Salida esperada:

Status: Please wait...
Success (200): User profile loaded
Error (404): Not found

Hoja de referencia

Las clases selladas (sealed classes) restringen qué clases pueden extenderlas o implementarlas. Solo las clases en el mismo archivo pueden ser subtipos directos, creando un conjunto cerrado de variantes conocidas.

sealed class Result {}

class Success extends Result {
  final String data;
  Success(this.data);
}

class Failure extends Result {
  final String error;
  Failure(this.error);
}

Las clases selladas permiten la comprobación de exhaustividad en las expresiones switch. El compilador conoce todos los subtipos posibles y garantiza que manejes cada uno de ellos:

String handleResult(Result result) {
  return switch (result) {
    Success(data: var d) => 'Got: $d',
    Failure(error: var e) => 'Error: $e',
  };
  // No default case needed - all cases are covered
}

Si agregas un nuevo subtipo, el compilador marcará cada sentencia switch que no lo maneje. Esto hace que las clases selladas sean ideales para modelar estados, resultados o cualquier conjunto fijo de variantes donde desees garantías en tiempo de compilación de que todos los casos están cubiertos.

Pruébalo tú mismo

import 'response.dart';

// TODO: Crea una función handleResponse(NetworkResponse response)
// que devuelva un String
// Usa una expresión switch con coincidencia de patrones (pattern matching) para manejar cada caso:
// - Para Loading: devuelve "Status: [message]"
// - Para Success: devuelve "Success ([statusCode]): [data]"
// - Para Error: devuelve "Error ([errorCode]): [errorMessage]"

void main() {
  // TODO: Crea una instancia de Loading con el mensaje "Please wait..."
  
  // TODO: Crea una instancia de Success con los datos "User profile loaded" y el código de estado 200
  
  // TODO: Crea una instancia de Error con el mensaje de error "Not found" y el código de error 404
  
  // TODO: Imprime el resultado de manejar cada respuesta
}
quiz iconPonte a prueba

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