Dart Interfaces y Mixin

Aunque Dart solo admite herencia única, a través de interfaces y Mixin se puede lograr de forma flexible la reutilización múltiple de código.

Este capítulo explica cómo usar implements para implementar interfaces, la reutilización de código con mixin y la palabra clave with.


implements para implementar interfaces

En Dart, cada clase define implícitamente una interfaz.

Usar la palabra clave implements permite que una clase implemente la interfaz de otra clase, e incluso puede implementar varias.

Ejemplo

// Definir una interfaz 'imprimible'
class Printable {
  // Los métodos de la interfaz no tienen implementación predeterminada
  // Quien la implemente debe sobrescribir todos los miembros
  void printContent() {
    // Este cuerpo de método se ignora al usar implements
  }
}

// Definir una interfaz 'guardable'
class Savable {
  void save() {}
}

// Usar implements para implementar múltiples interfaces
class Document implements Printable, Savable {
  String title;
  String content;

  Document(this.title, this.content);

  // Debe implementar todos los métodos requeridos por la interfaz Printable
  @override
  void printContent() {
    print('--- Documento: $title ---');
    print(content);
    print('--- EJEMPLO fin de impresión ---');
  }

  // Debe implementar todos los métodos requeridos por la interfaz Savable
  @override
  void save() {
    print('El documento "$title" se ha guardado en el disco');
  }
}

void main() {
  var doc = Document('Tutorial de Dart', 'Esta es una guía de introducción a Dart');

  // Polimorfismo: Document es tanto Printable como Savable
  Printable printable = doc;
  printable.printContent();

  Savable savable = doc;
  savable.save();
}
--- 文档: Dart 教程 ---
这是一份 Dart 入门指南
--- EJEMPLO 打印结束 ---
文档 "Dart 教程" 已保存到磁盘

La diferencia clave entre implements y extends: extends hereda todas las implementaciones de la clase padre (cuerpos de métodos), mientras que implements solo hereda las firmas de la interfaz, por lo que debes reimplementar todos los métodos. Además, extends solo puede heredar de una clase, mientras que implements puede implementar múltiples interfaces.

Comparación de extends vs implements

Característicaextendsimplements
Límite de cantidadSolo puede heredar unoPuede implementar varios
Implementación de métodosHereda la implementación de la clase padreDebe implementar todos los métodos por sí mismo
ConstructorPuede llamar a super()No hereda constructores
Casos de usoRelación "es un"Contrato de "qué puede hacer"

Implementación de múltiples interfaces

Dart puede implementar múltiples interfaces; esta es la forma principal de superar la limitación de la herencia única.

Ejemplo

// Definir múltiples interfaces de comportamiento
class Flyable {
  void fly() {}
}

class Swimmable {
  void swim() {}
}

class Walkable {
  void walk() {}
}

// Un pato puede volar, nadar y caminar
class Duck implements Flyable, Swimmable, Walkable {
  String name;

  Duck(this.name);

  @override
  void fly() {
    print('$name vuela por el cielo');
  }

  @override
  void swim() {
    print('$name nada sobre el agua');
  }

  @override
  void walk() {
    print('$name camina tambaleándose por tierra');
  }
}

// Un pez solo puede nadar
class Fish implements Swimmable {
  @override
  void swim() {
    print('El pez nada en el agua');
  }
}

void main() {
  var duck = Duck('Pato EJEMPLO');

  // El pato puede desempeñar múltiples roles
  (duck as Flyable).fly();
  (duck as Swimmable).swim();
  (duck as Walkable).walk();

  // Comprobación de tipos
  print('¿Puede el pato volar? ${duck is Flyable}');
  print('¿Puede el pato nadar? ${duck is Swimmable}');

  var fish = Fish();
  print('¿Puede el pez volar? ${fish is Flyable}');
}
EJEMPLO 鸭 在天空中飞翔
EJEMPLO 鸭 在水面上游泳
EJEMPLO 鸭 在陆地上摇摇晃晃地走
鸭子能飞吗? true
鸭子能游吗? true
鱼能飞吗? false

Reutilización de código con Mixin

Mixin es un mecanismo para reutilizar código entre múltiples clases; resuelve el problema de "varias clases necesitan compartir los mismos métodos pero no es adecuado usar herencia".

Usa la palabra clave mixin para definir un Mixin y la palabra clave with para mezclarlo en una clase.

Ejemplo

// Definir usando la palabra clave mixin
mixin Logger {
  // Los métodos del Mixin pueden ser reutilizados por múltiples clases
  void log(String message) {
    print('[${DateTime.now()}] $message');
  }

  void logError(String message) {
    print('[ERROR ${DateTime.now()}] $message');
  }
}

mixin TimestampFormatter {
  String formatTimestamp(DateTime dt) {
    return '${dt.year}-${dt.month.toString().padLeft(2, '0')}'
        '-${dt.day.toString().padLeft(2, '0')}';
  }
}

// Usar la palabra clave with para mezclar el Mixin
class UserService with Logger, TimestampFormatter {
  void createUser(String name) {
    log('Crear usuario: $name');
    var now = DateTime.now();
    print('Fecha de creación: ${formatTimestamp(now)}');
  }
}

class OrderService with Logger {
  void createOrder(String orderId) {
    log('Crear pedido: $orderId');
  }

  void cancelOrder(String orderId) {
    logError('Error al cancelar el pedido: $orderId');
  }
}

void main() {
  var userService = UserService();
  userService.createUser('ejemplo');

  var orderService = OrderService();
  orderService.createOrder('ORD-001');
  orderService.cancelOrder('ORD-001');
}
[2026-06-12 10:30:00.000] 创建用户: ejemplo
创建时间: 2026-06-12
[2026-06-12 10:30:00.001] 创建订单: ORD-001
[ERROR 2026-06-12 10:30:00.001] 取消订单失败: ORD-001

UserService y OrderService provienen de diferentes dominios de negocio, por lo que no es adecuado usar herencia para compartir Logger.

Mediante Mixin, ambos pueden obtener la funcionalidad de registro (logging) sin afectarse entre sí.

Mixin no puede tener constructores ni puede ser instanciado. Su propósito es muy puro: proporcionar métodos reutilizables, sin implicar inicialización de estado.

Restricciones de Mixin

Mixin puede usar la palabra clave on para restringir que solo pueda ser utilizado por ciertos tipos de clases.

Ejemplo

// Clase base
class Animal {
  String name;
  Animal(this.name);
}

// Este Mixin solo puede mezclarse en Animal y sus subclases
mixin FlyableMixin on Animal {
  void fly() {
    print('$name ha volado!');
  }
}

// Bird es un Animal, puede usar FlyableMixin
class Bird extends Animal with FlyableMixin {
  Bird(String name) : super(name);
}

// La siguiente línea dará error: Car no es Animal, no puede usar FlyableMixin
// class Car with FlyableMixin {} // ¡Error!

void main() {
  var bird = Bird('Pajarito EJEMPLO');
  bird.fly();

  // Los métodos de FlyableMixin pueden acceder a la propiedad name de Animal
  print('El nombre del pajarito: ${bird.name}');
}
EJEMPLO 小鸟 飞起来了!
小鸟的名字: EJEMPLO 小鸟

Combinación completa de class, Mixin, extends e implements

En Dart, una clase puede usar extends, with e implements al mismo tiempo:

Ejemplo

mixin Jumpable {
  void jump() => print('¡Salta!');
}

mixin Runnable {
  void run() => print('¡Corre hacia adelante!');
}

class Animal {
  void breathe() => print('Respirando...');
}

// Heredar de Animal, mezclar Jumpable y Runnable, implementar Comparable
class Athlete extends Animal
    with Jumpable, Runnable
    implements Comparable<Athlete> {
  String name;
  int score;

  Athlete(this.name, this.score);

  @override
  int compareTo(Athlete other) => score.compareTo(other.score);

  void showSkills() {
    breathe();
    run();
    jump();
  }
}

void main() {
  var a1 = Athlete('Competidor A de EJEMPLO', 95);
  var a2 = Athlete('Competidor B', 88);

  a1.showSkills();
  print('${a1.name} vs ${a2.name}: ${a1.compareTo(a2) > 0 ? "A gana" : "B gana"}');
}
呼吸...
跑步前进!
跳起来!
EJEMPLO 选手A vs 选手B: A 胜

Orden completo de declaración:class Subclase extends ClasePadre with Mixin1, Mixin2 implements Interfaz1, Interfaz2

Otras extensiones