Menu
Coddy logo textTech

Mixin vs Interfaz

Parte de la sección Programación Orientada a Objetos del Journey de Dart de Coddy — lección 55 de 110.

Has visto cómo se comparan los mixins con la herencia. Ahora comparemos los mixins con las interfaces, ya que ambos permiten trabajar con múltiples tipos. La diferencia clave es lo que proporcionan: las interfaces definen un contrato, mientras que los mixins proporcionan una implementación.

Cuando implementas una interfaz, prometes proporcionar ciertos métodos, pero escribes todo el código tú mismo. Cuando usas un mixin, obtienes el código real de forma gratuita.

// Enfoque de interfaz: debes implementar todo
abstract class Printable {
  void printInfo();
}

class Report implements Printable {
  @override
  void printInfo() {
    print('Printing report...');  // Tú escribes esto
  }
}

// Enfoque de mixin: implementación incluida
mixin Printable {
  void printInfo() {
    print('Printing...');  // Ya escrito para ti
  }
}

class Report with Printable {}  // Obtiene printInfo() automáticamente

¿Entonces, cuándo deberías usar cada uno? Usa una interfaz cuando diferentes clases necesiten las mismas firmas de método pero diferentes implementaciones. Usa un mixin cuando las clases deban compartir exactamente el mismo comportamiento.

CaracterísticaInterfazMixin
ProporcionaSolo contratoContrato + código
ImplementaciónTú la escribesIncluida
Palabra claveimplementswith
Ideal paraDiferentes comportamientos, misma APIComportamiento compartido

Incluso puedes combinar ambos enfoques: usa interfaces para definir lo que una clase puede hacer y mixins para proporcionar implementaciones comunes cuando sea apropiado. En Dart, al combinar ambos, coloca siempre with antes de implements:

class MyClass with SomeMixin implements SomeInterface {
  // with va antes de implements
}
challenge icon

Desafío

Fácil

Vamos a construir un sistema de notificaciones que demuestre cuándo usar interfaces frente a mixins. Crearás una interfaz para notificaciones que requieren diferentes implementaciones y un mixin para el comportamiento de registro (logging) compartido que debe ser idéntico en todos los tipos de notificación.

Organizarás tu código en tres archivos:

  • notifiable.dart: Define una clase abstracta Notifiable que actúe como una interfaz. Debe declarar dos métodos abstractos: send(String message) y getChannel() que devuelva un String. Los diferentes tipos de notificación implementarán estos métodos de manera distinta según su canal específico.
  • logging.dart: Crea un mixin Logging que proporcione el comportamiento compartido que todas las notificaciones deben tener. Incluye un método logSent(String channel, String message) que imprima [LOG] Sent via [channel]: [message] y un método logTimestamp() que imprima [LOG] Timestamp recorded. Este comportamiento es idéntico para todos los tipos de notificación, lo que lo hace perfecto para un mixin.
  • main.dart: Importa ambos archivos y crea dos clases de notificación que combinen la interfaz con el mixin:
    • Una clase EmailNotification que use el mixin Logging e implemente Notifiable. Su getChannel() debe devolver 'Email', y su método send() debe imprimir Sending email: [message], luego llamar a logSent() con el canal y el mensaje, y finalmente llamar a logTimestamp().
    • Una clase SMSNotification que use el mixin Logging e implemente Notifiable. Su getChannel() debe devolver 'SMS', y su método send() debe imprimir Sending SMS: [message], luego llamar a logSent() con el canal y el mensaje, y finalmente llamar a logTimestamp().
    Luego crea un EmailNotification y llama a send('Hello World'), imprime una línea vacía, crea un SMSNotification y llama a send('Quick update').

Observa cómo la interfaz obliga a cada clase a escribir sus propias implementaciones de send() y getChannel() (comportamiento diferente), mientras que el mixin proporciona los métodos de registro que funcionan de manera idéntica para ambos (comportamiento compartido).

Salida esperada:

Sending email: Hello World
[LOG] Sent via Email: Hello World
[LOG] Timestamp recorded

Sending SMS: Quick update
[LOG] Sent via SMS: Quick update
[LOG] Timestamp recorded

Hoja de referencia

Las interfaces definen un contrato (firmas de métodos), mientras que los mixins proporcionan la implementación (código real).

Cuando implementas una interfaz, debes escribir todo el código tú mismo:

abstract class Printable {
  void printInfo();
}

class Report implements Printable {
  @override
  void printInfo() {
    print('Printing report...');  // Tú escribes esto
  }
}

Cuando usas un mixin, obtienes la implementación de forma gratuita:

mixin Printable {
  void printInfo() {
    print('Printing...');  // Ya escrito para ti
  }
}

class Report with Printable {}  // Obtiene printInfo() automáticamente

Cuándo usar cada uno:

  • Usa una interfaz cuando diferentes clases necesiten las mismas firmas de métodos pero diferentes implementaciones
  • Usa un mixin cuando las clases deban compartir exactamente el mismo comportamiento
CaracterísticaInterfazMixin
ProporcionaSolo contratoContrato + código
ImplementaciónTú la escribesIncluida
Palabra claveimplementswith
Ideal paraDiferentes comportamientos, misma APIComportamiento compartido

Puedes combinar ambos enfoques. Al combinar, siempre coloca with antes de implements:

class MyClass with SomeMixin implements SomeInterface {
  // with va antes de implements
}

Pruébalo tú mismo

import 'notifiable.dart';
import 'logging.dart';

// TODO: Crear la clase EmailNotification que:
//       - use el mixin Logging (with)
//       - implemente Notifiable (implements)
//       - getChannel() devuelva 'Email'
//       - send() imprima 'Sending email: [message]', luego llame a logSent(), y después a logTimestamp()

// TODO: Crear la clase SMSNotification que:
//       - use el mixin Logging (with)
//       - implemente Notifiable (implements)
//       - getChannel() devuelva 'SMS'
//       - send() imprima 'Sending SMS: [message]', luego llame a logSent(), y después a logTimestamp()

void main() {
  // TODO: Crear una EmailNotification y llamar a send('Hello World')
  
  // TODO: Imprimir una línea vacía
  
  // TODO: Crear una SMSNotification y llamar a send('Quick update')
}
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación Orientada a Objetos