🧩 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
ouPodeNadar
. - 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:
➕ 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:
➕ 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: