Mixin vs Herencia
Parte de la sección Programación Orientada a Objetos del Journey de Dart de Coddy — lección 54 de 110.
Ahora que has aprendido cómo funcionan los mixins, aclaremos cuándo usarlos en lugar de la herencia. La diferencia clave radica en la relación que expresan.
La herencia representa una relación "es-un". Un Dog es un Animal. La subclase es una versión especializada del padre, y solo puedes extender una clase.
Los mixins, por otro lado, representan una relación "puede-hacer". Un Dog puede nadar, pero nadar no es lo que define a un perro.
// Herencia: Dog ES un Animal
class Animal {
String name;
Animal(this.name);
}
class Dog extends Animal {
Dog(String name) : super(name);
}
// Mixin: Dog PUEDE nadar (pero no está definido por nadar)
mixin Swimming {
void swim() => print('Swimming');
}
class Dog extends Animal with Swimming {
Dog(String name) : super(name);
}Usa la herencia cuando las clases compartan una identidad fundamental y necesites constructores en el padre. Usa mixins cuando quieras añadir capacidades a clases que no comparten un ancestro común.
| Característica | Herencia | Mixin |
|---|---|---|
| Relación | es-un | puede-hacer |
| Límite | Solo un padre | Múltiples mixins |
| Constructores | Permitido | No permitido |
| Caso de uso | Especialización | Comportamiento compartido |
¿La mejor parte? Puedes combinar ambos: extender una clase y mezclar múltiples capacidades, dándole a tus clases exactamente las características que necesitan.
Desafío
FácilVamos a construir un sistema de trabajadores que demuestre cuándo usar la herencia frente a los mixins. Crearás una clase base para la relación "es-un" (is-a) y mixins para las capacidades de "puede-hacer" (can-do), luego los combinarás para mostrar cómo ambos enfoques funcionan juntos.
Organizarás tu código en tres archivos:
worker.dart: Define tu clase base que establece la identidad central de todos los trabajadores:- Una clase
Workercon las propiedadesString nameyString department. Incluye un constructor que reciba ambos valores. Añade un métodointroduce()que imprima'$name works in $department'
- Una clase
skills.dart: Define mixins que representen las capacidades que los trabajadores pueden tener (las relaciones "puede-hacer"):- Un mixin
CanCodecon un métodowriteCode()que imprima'Writing code...' - Un mixin
CanDesigncon un métodocreateDesign()que imprima'Creating design...' - Un mixin
CanManagecon un métodoleadTeam()que imprima'Leading the team...'
- Un mixin
main.dart: Importa ambos archivos y crea tipos de trabajadores especializados que combinen la herencia con los mixins:- Una clase
Developerque extienda deWorkery use el mixinCanCode - Una clase
TechLeadque extienda deWorkery use los mixinsCanCodeyCanManage - Crea un
Developerllamado'Alice'en el departamento'Engineering' - Crea un
TechLeadllamado'Bob'en el departamento'Engineering' - Para Alice: llama a
introduce()y luego awriteCode() - Imprime una línea vacía
- Para Bob: llama a
introduce(), luego awriteCode()y después aleadTeam()
- Una clase
Observa cómo tanto Developer como TechLead son trabajadores (herencia), pero pueden hacer cosas diferentes (mixins). El TechLead combina múltiples capacidades sin necesidad de herencia múltiple.
Salida esperada:
Alice works in Engineering
Writing code...
Bob works in Engineering
Writing code...
Leading the team...Hoja de referencia
La herencia representa una relación "es-un" (una subclase es una versión especializada del padre), mientras que los mixins representan una relación "puede-hacer" (añadiendo capacidades sin definir una identidad).
// Inheritance: Dog IS an Animal
class Animal {
String name;
Animal(this.name);
}
class Dog extends Animal {
Dog(String name) : super(name);
}
// Mixin: Dog CAN swim
mixin Swimming {
void swim() => print('Swimming');
}
class Dog extends Animal with Swimming {
Dog(String name) : super(name);
}| Característica | Herencia | Mixin |
|---|---|---|
| Relación | es-un | puede-hacer |
| Límite | Solo un padre | Múltiples mixins |
| Constructores | Permitidos | No permitidos |
| Caso de uso | Especialización | Comportamiento compartido |
Puedes combinar ambos enfoques: extender una clase y mezclar múltiples capacidades:
class TechLead extends Worker with CanCode, CanManage {
TechLead(String name, String department) : super(name, department);
}Pruébalo tú mismo
import 'worker.dart';
import 'skills.dart';
// TODO: Crea una clase Developer que extienda de Worker y use el mixin CanCode
// TODO: Crea una clase TechLead que extienda de Worker y use los mixins CanCode y CanManage
void main() {
// TODO: Crea un Developer llamado 'Alice' en el departamento 'Engineering'
// TODO: Llama a introduce() y luego a writeCode() para Alice
// TODO: Imprime una línea vacía
// TODO: Crea un TechLead llamado 'Bob' en el departamento 'Engineering'
// TODO: Llama a introduce(), writeCode(), y luego a leadTeam() para Bob
}
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
1Fundamentos de POO
Archivos externosLibrerías e importacionesIntroducción a la POOClases vs. ObjetosLa palabra clave thisMétodosVariables de instanciaConceptos básicos de constructoresResumen - Calculadora sencilla4Seguridad de nulos
Introducción a la seguridad de nulosNulables vs No nulablesLos operadores ? y !Palabra clave Late y Null SafetyOperadores Null-awareNull Safety en clasesResumen - Sistema de perfil de usuario7Clases abstractas e interfaces
Clases abstractasMétodos abstractosInterfaces en DartInterfaces implícitasImplementar vs. ExtenderInterfaces múltiplesRepaso - Calculadora de figuras10Colecciones y Genéricos
Visión general de List, Set y MapColecciones con seguridad de tiposClases genéricasMétodos genéricosRestricciones genéricasIterable e IteratorResumen - Almacenamiento genérico2Constructores en Dart
Constructor por defectoConstructores nombradosListas de inicializaciónConstructores constantesConstructores de fábricaConstructores de redireccionamientoResumen - Constructor de formas5Encapsulamiento
Miembros públicos vs. privadosLa convención del prefijo _Privacidad a nivel de libreríaProfundidad en Getters y SettersOcultamiento de informaciónResumen - Registros de estudiantes8Mixins
Introducción a los MixinsCreación de MixinsUso de múltiples MixinsPalabra clave on en MixinsMixin vs HerenciaMixin vs InterfazResumen - Sistema Animal11Métodos especiales
Sobrescritura de toString()Sobrescritura de hashCode y ==Interfaz ComparableMétodo call()Sobrescritura de noSuchMethodResumen - Colección personalizada14Patrones de diseño Parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón ObserverPatrón Strategy3Propiedades de clase
Miembros de instancia vs. estáticosCampos Final y ConstVariables LateMétodos y campos estáticosGetters y SettersResumen - Gestor de cuentas bancarias6Herencia
Herencia básicaLa palabra clave superSobrescritura de métodosLa anotación @overrideLa palabra clave final en clasesConstructores y herenciaResumen - Jerarquía de empleados9Polimorfismo
Fundamentos del polimorfismoPolimorfismo mediante interfacesComprobación de tipos en tiempo de ejecuciónLos operadores is y asPalabra clave covariantResumen - Procesador de pagos12POO Asíncrona
Futures y async/awaitFundamentos de StreamsStream ControllersConstructores asíncronosAsincronía en métodos de claseRepaso - Data Fetcher15Patrones de diseño Parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón CompositePatrón Repository