Dart — Flujo de control (Branches)


Ramas (Branches)

Las ramas de flujo de control en Dart, se refieren a las sentencias o declaraciones condicionales que permiten tomar diferentes caminos en la ejecución de un programa. Con lo anterior vemos que con branches podemos controlar el flujo de nuestro código. 

Los condicionales manejados en las ramas de flujo de control son las siguientes:

  • if 
  • if-case 
  • switch 

If

En Dart, los condicionales if funcionan como en casi todos los demás lenguajes de programación, van acompañados de condicional else que es opcional. Luego de la palabra if va entre paréntesis una condición que debe dar como resultado un booleano, en caso de que no se cumpla la condición del if podemos usar el else para que haga o ejecute otra lógica.

void main() {
  var num1 = 5;

  // valida si el número es mayor a 5
  if (num1 > 5) {
    print('es mayor');
  } else if (num1 == 5) { // valida si el número es igual a 5
    print('es igual');
  } else {
    // sino cumple ninguna de las anteriores es porque num1 es menor a 5
    print('es menor');
  }
}

También se pueden utilizar los if con operadores ternarios.

void main() {
  bool isDark = false;
  // aquí si isDark es true imprime black sino imprime white
  var valid = isDark ? 'black' : 'white';

  print(valid);
}

If-case

La declaración if-case se agregó en la versión 3.0 de Dart, funciona similar que los switch pero un alcance más corto, en caso de colocar varios if-case se recomienda usar switch.

void main() {
  var pair = [3.3, 20];
  // valida que pair cumpla con el caso de que son 2 enteros.
  // sino lanza una excepción.
  if (pair case [int x, int y]) {
    print('coordenadas X:$x y Y:$y');
  } else {
    throw FormatException('Coordenadas inválidas!');
  }
}

Sentencia Switch

Una sentencia switch evalúa una expresión contra diferentes casos. Cada caso evalúa que el valor coincida por medio de un patrón. Cuando este valor coincide se ejecuta cierta lógica particular. Tiene varias sentencias (continue, throw, return) para interrumpir la validación de todos los casos y también se puede usar al final un default, cuando no cumple con ningún caso anterior.

// ejemplo básico de switch
void main() {
  var color = 'GREEN';

  switch (color) {
    case 'RED':
      print('color RED');
    case 'BLUE':
      print('color BLUE');
    case 'YELLOW':
      print('color YELLOW');
    case 'GREEN':
      print('color GREEN');
    default:
      print('color no encontrado');
  }
}

Los case vacíos que no tienen lógica para ejecutar, se pasan de largo y continúan al siguiente caso, es como si fuera un OR (blue || green). Se pueden saltar de un caso a otro, con la sentencia continue y una etiqueta.

switch (color) {
  case 'RED':
    print('color RED');
    continue newCase; // cuando color sea RED, imprime color RED
                                       // y ejecuta el continue newCase

  case 'BLUE': // Caso vacío
  case 'GREEN':
    print('colores BLUE, GREEN'); // se ejecuta tanto para BLUE como para GREEN

  newCase:
  case 'YELLOW':
    print('color YELLOW'); // Se ejecuta luego de RED pero también para YELLOW.
}

Expresiones Switch

Una expresión switch, devuelve un valor basado en lo que ejecuta el caso que coincide. En estos casos no se puede usar expresiones.

var y = 'D';
var x = switch (y) { 'D' => 'd' };

print(switch (x) { 'D' => 'd' });

return switch (x) { 'D' => 'd' };

Tenemos un ejemplo para comparar cómo se ven las sentencias switch y expresiones switch.

const String one = '1';
const String two = '2';
const String three = '3';
const String four = '4';
const String five = '5';
const String a = 'A';
const String b = 'B';
const String c = 'C';

void main() {
  String input = '5';
  String switch1, switch2;

  // ejemplo con sentencia switch
  switch (input) {
    case one || two || three || four || five:
      switch1 = 'Número $input';
    case a || b || c:
      switch1 = 'Letra $input';
    default:
      throw const FormatException('Invalid');
}

// ejemplo con expresión switch
switch2 = switch (input) {
  one || two || three || four || five => 'Número $input',
  a || b || c => 'Letra $input',
  _ => throw const FormatException('Invalid')
};

print(switch1);
print(switch2);
}

Control exhaustivo

La verificación exhaustiva es una característica que informa un error de tiempo de compilación, allí es importante el uso de default en las sentencias switch o _ en las expresiones switch.

bool nullableBool;
// aquí nos muestra error en tiempo de compilación
// porque puede ser null
switch (nullableBool) {
case true:
print('yes');
case false:
print('no');
}

Los enums y los tipos sellados (sealed types) son muy útiles para los switch porque, siempre van a tener un valor con el cual coincidir. 

// ejemplo con enums
enum Color { yellow, blue, red}
void main() {
  var color = Color.red;
  var isPrimary = switch (color) {
    Color.yellow || Color.blue || Color.red => true,
    _ => false
  };

  print(isPrimary);
}

// ejemplo con sealed
sealed class Animal {
  String action();
}

class Fish implements Animal {
  @override
  String action() {
    return 'puede nadar';
  }
}

class Bird implements Animal {
  @override
  String action() {
    return 'puede volar';
  }
}

String validateAction(Animal animal) => switch (animal) {
    Fish() => 'El pez ${animal.action()}',
    Bird() => 'El ave ${animal.action()}'
};

void main() {
  var fish = Fish();
  print(validateAction(fish));
}

Cláusula de protección (Guard clause)

Para establecer una cláusula de protección opcional después de una cláusula de caso, use la palabra clave when. Para más información revisar aquí.

A continuación, un ejemplo que nos brinda la documentación de Dart.

// Expresiones switch
var value = switch (something) {
  somePattern when some || boolean || expression => body,
// colocamos la cláusula de guardia aquí
}

Conclusión

Vimos los flujos de control de ramas (branches) que utiliza Dart para controlar el código de nuestras aplicaciones.

Siguientes pasos

En la siguiente publicación seguiremos aprendiendo sobre manejo de errores en Dart.

Fuentes

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *