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
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ística | extends | implements |
|---|---|---|
| Límite de cantidad | Solo puede heredar uno | Puede implementar varios |
| Implementación de métodos | Hereda la implementación de la clase padre | Debe implementar todos los métodos por sí mismo |
| Constructor | Puede llamar a super() | No hereda constructores |
| Casos de uso | Relació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
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
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
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
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