diegoRodicio

Está documentación está a túa disposición sin ningún custo económico. Sen embargo, para a súa elaboración dedico moito tempo e recursos, polo que agradecería unha colaboración co que consideres oportuno. Gracias.

View Categories

🧠 Para saber mais

🧩 Mixins #

Os Mixins son unha forma de reutilizar código. Permiten engadir funcionalidades a unha clase sen usar a herdanza tradicional. É unha forma moi práctica de compartir funcionalidades entre clases non relacionadas.

  • Un mixin é como unha clase que proporciona unha implementación de métodos (e opcionalmente atributos) que poden ser “mesturados” noutras clases.
  • A diferenza da herdanza (onde unha subclase extends unha única superclase), unha clase pode usar múltiples mixins. Isto é útil porque Dart non soporta a herdanza múltiple de clases, pero os mixins permiten compartir comportamentos entre xerarquías de clases non relacionadas.

En Dart, os mixins úsanse coa palabra clave with, e defínense coa palabra mixin.

🔸 Características e Uso dos Mixins #

Reutilización de Código: Permiten reutilizar a implementación de métodos en varias clases sen crear unha xerarquía de herdanza ríxida.

  • Comportamento Compartido: Son ideais para engadir comportamentos específicos que non definen “que é” un obxecto, senón “o que pode facer”. Por exemplo, PodeVoar ou PodeNadar.
  • Composición: Favorecen a composición sobre a herdanza, facendo o código máis flexible.
mixin Musical {
  bool sabeTocarPiano = false;

  void interpretar() {
    if (sabeTocarPiano) {
      print('Tocando o piano...');
    } else {
      print('Cantando coa boca pechada...');
    }
  }
}

// Clase que usa o mixin Musical
class Persoa with Musical {}

void main() {
  var artista = Persoa();
  artista.sabeTocarPiano = true;
  artista.interpretar(); // Output: Tocando o piano...
}

Exemplo #

// Definición dun Mixin 'PodeVoar'
mixin PodeVoar {
  void voar() {
    print('Estou voando alto!');
  }
}

// Definición dun Mixin 'PodeCamiñar'
mixin PodeCamiñar {
  void camiñar() {
    print('Estou camiñando.');
  }
}

// Clase que usa os Mixins
class Paxaro with PodeVoar, PodeCamiñar {
  String nome;

  Paxaro(this.nome);

  void piar() {
    print('$nome está piando.');
  }
}

class Persoa with PodeCamiñar {
  String nome;

  Persoa(this.nome);

  void falar() {
    print('$nome está a falar.');
  }
}

void main() {
  var aguia = Paxaro('Águia');
  aguia.piar();   // Método propio da clase Paxaro
  aguia.voar();   // Método do mixin PodeVoar
  aguia.camiñar(); // Método do mixin PodeCamiñar

  print('---');

  var manuel = Persoa('Manuel');
  manuel.falar();     // Método propio da clase Persoa
  manuel.camiñar();   // Método do mixin PodeCamiñar
  // manuel.voar(); // ❌ Erro: 'Persoa' non usa o mixin 'PodeVoar'
}

📚 Para saber máis: #

Nestes apuntamentos limítome a explicar o uso básico dun mixin. Porén, Dart ofrece moitas máis posibilidades e características avanzadas para traballar con mixins.

Se queres afondar máis no tema, podes consultar:

Mixins na documentación oficial de Dart

➕ Métodos de Extensión #

Dart permite engadir funcionalidade a clases xa existentes mediante o uso de métodos de extensión, sen necesidade de modificar a clase orixinal nin crear subclases.

Este mecanismo é moi útil cando queremos engadir métodos a tipos existentes (como String, int, List, etc.), especialmente se non temos acceso ao seu código fonte (por exemplo, en clases do SDK ou de terceiros).

🔹 Uso de Métodos de Extensión #

Para crear un método de extensión, utilízase a palabra clave extension

Así, por exemplo, imos engadir un novo método parseInt() á clase String, que convirte a cadea nun número enteiro:

// Definimos unha extensión sobre String
extension ConversorEnteiro on String {
  int parseInt() {
    return int.parse(this);
  }
}

void main() {
  print('42'.parseInt()); // Output: 42
}

🔍 Este método pódese usar coma se fose parte da clase String, sempre que o importes se está noutro ficheiro.

🔹 Exemplo básico #

// Exemplo 1: Engadir un método a String
extension UtilesDeTexto on String { // 'on String' indica que esta extensión se aplica á clase String
  String reverter() {
    return this.split('').reversed.join('');
  }

  bool ePalindromo() {
    final textoLimpo = this.toLowerCase().replaceAll(RegExp(r'[^a-z0-9]'), '');
    return textoLimpo == textoLimpo.reverter(); // Usa o método reverter() da mesma extensión
  }
}

// Exemplo 2: Engadir un método a List<int>
extension CalculosDeLista on List<int> {
  int sumaTotal() {
    var suma = 0;
    for (var n in this) {
      suma += n;
    }
    return suma;
  }

  double media() {
    if (this.isEmpty) return 0.0;
    return this.sumaTotal() / this.length;
  }
}

void main() {
  // Usando a extensión en String
  var mensaxe = "Ola Mundo";
  print('Texto orixinal: "$mensaxe"');
  print('Texto revertido: "${mensaxe.reverter()}"'); // Usa o método de extensión

  var palabra1 = "radar";
  var palabra2 = "Dart";
  print('"${palabra1}" é palíndromo? ${palabra1.ePalindromo()}'); // true
  print('"${palabra2}" é palíndromo? ${palabra2.ePalindromo()}'); // false

  // Usando a extensión en List<int>
  var notas = [8, 9, 7, 10];
  print('Lista de notas: $notas');
  print('Suma total das notas: ${notas.sumaTotal()}'); // Usa o método de extensión
  print('Media das notas: ${notas.media()}');
}

📚 Para saber máis #

Os métodos de extensión teñen máis funcionalidades, como xestionar conflitos ou traballar con tipos xenéricos. Para afondar máis no seu uso e consideracións, recoméndase consultar a documentación oficial de Dart sobre Métodos de Extensión:

Métodos de extensión na documentación oficial de Dart →

➕ Tipos de Extensión #

Os tipos de extensión son unha característica avanzada de Dart que permite crear un tipo novo baseado noutro existente, pero redefinindo a súa interface de forma segura e estática.

A diferenza dos métodos de extensión, que engaden métodos a un tipo existente, os tipos de extensión permiten crear un novo tipo que encapsula outro, expoñendo só os membros desexados e ocultando os demais. Todo isto faise sen custo en tempo de execución, xa que só existe en tempo de compilación.

🔹 Uso de Tipos de Extensión #

Para crear un tipo de extensión, utilízase a palabra clave extension type:

// Exemplo 1: Un tipo de extensión para representar unha ID de Usuario
// A ID real é un String, pero engadimos validación e un método de formato.
extension type IdUsuario(String valor) { // 'String valor' é o tipo de representación
  // Construtor nomeado que valida o formato da ID
  factory IdUsuario.validar(String id) {
    if (id.length != 8 || !RegExp(r'^[a-zA-Z0-9]+$').hasMatch(id)) {
      throw FormatException('A ID de usuario debe ter 8 caracteres alfanuméricos.');
    }
    return IdUsuario(id); // Devolve unha nova instancia do tipo de extensión
  }

  // Método de instancia
  String formatoCurto() {
    return valor.substring(0, 4) + '...';
  }

  // Getter de instancia
  bool get eValido => valor.length == 8 && RegExp(r'^[a-zA-Z0-9]+$').hasMatch(valor);

}

// Exemplo 2: Un tipo de extensión para representar unha cantidade en Euros
// O valor real é un 'double', pero queremos forzar que sexa unha moeda.
extension type Euros(double cantidade) { // 'double cantidade' é o tipo de representación
  // Construtor que asegura que o valor non é negativo
  factory Euros.desdeCero(double val) {
    if (val < 0) {
      throw ArgumentError('A cantidade de euros non pode ser negativa.');
    }
    return Euros(val);
  }

  // Operador sobrecargado para sumar euros
  Euros operator +(Euros outraCantidade) {
    return Euros(cantidade + outraCantidade.cantidade);
  }

  // Método para formatar a saída
  String formatar() {
    return '${cantidade.toStringAsFixed(2)} €';
  }
}

void main() {
  // Usando o tipo de extensión IdUsuario
  try {
    var id1 = IdUsuario.validar('abcde123'); // Usa o construtor factory
    print('ID de usuario: ${id1.valor}');
    print('ID en formato curto: ${id1.formatoCurto()}');
    print('ID é válida? ${id1.eValido}');

    // var idInvalida = IdUsuario.validar('abc'); // Lanzaría FormatException
  } catch (e) {
    print('Erro coa ID: $e');
  }

  // Usando o tipo de extensión Euros
  var prezoArtigo = Euros.desdeCero(19.99);
  var imposto = Euros.desdeCero(4.20);
  var total = prezoArtigo + imposto; // Usa o operador sobrecargado '+'

  print('Prezo do artigo: ${prezoArtigo.formatar()}');
  print('Imposto: ${imposto.formatar()}');
  print('Total: ${total.formatar()}');

  // var saldoNegativo = Euros.desdeCero(-5.0); // Lanzaría ArgumentError
}

📚 Máis Información #

Este é só un exemplo básico. Os tipos de extensión permiten moito máis: definición de métodos, operadores, construtores nomeados, implementación de interfaces, uso de @redeclare, e moito máis.

Para explorar en profundidade as súas capacidades e escenarios de uso, podes consultar a documentación oficial de Dart:

Tipos de Extensión