Implementar vs. Extender
Parte de la sección Programación Orientada a Objetos del Journey de Dart de Coddy — lección 47 de 110.
Ahora que entiendes tanto extends como implements, aclaremos cuándo usar cada uno.
Utiliza extends cuando quieras heredar comportamiento. Tu subclase obtiene todo el código del padre de forma gratuita y puede construir sobre él:
class Animal {
void breathe() {
print('Breathing...');
}
}
class Dog extends Animal {
void bark() {
print('Woof!');
}
}
void main() {
var dog = Dog();
dog.breathe(); // Heredado - funciona automáticamente
dog.bark();
}Utiliza implements cuando quieras garantizar una estructura sin heredar código. Debes escribir cada método tú mismo:
class Robot implements Animal {
@override
void breathe() {
print('Ventilating circuits...'); // Implementación completamente personalizada
}
}La diferencia clave: extends dice "soy un tipo de esto" mientras que implements dice "puedo hacer lo que esto hace". Un Dog es un Animal y respira de la misma manera. Un Robot no es un animal, pero puede cumplir el mismo contrato con su propia lógica.
Recuerda: solo puedes extender una clase, pero puedes implementar muchas interfaces. Elige extends para relaciones de herencia verdaderas, y implements cuando necesites flexibilidad o quieras imponer un contrato sin compartir detalles de implementación.
Desafío
FácilVamos a construir un sistema de trabajadores que demuestre cuándo usar extends frente a implements. Crearás una clase base de trabajador y luego mostrarás ambos enfoques: una clase que realmente "es un" trabajador y hereda el comportamiento, y otra que simplemente "puede hacer" lo que hace un trabajador pero con una lógica completamente personalizada.
Organizarás tu código en dos archivos:
worker.dart: Define tu jerarquía de trabajadores aquí:- Una clase
Workercon una propiedadString namey un constructor. Incluye un métodowork()que imprima'$name is working...'y un métodotakeBreak()que imprima'$name takes a coffee break' - Una clase
OfficeWorkerque extienda (extends)Worker. Añade una propiedadString department. Sobrescribework()para que imprima'$name is typing reports in $department'pero mantén el comportamiento heredado detakeBreak() - Una clase
RobotWorkerque implemente (implements)Worker. Añade una propiedadString model. Dado que los robots no son realmente trabajadores pero pueden hacer lo que hacen los trabajadores, proporciona implementaciones completamente personalizadas:work()debe imprimir'Robot $model is assembling parts'ytakeBreak()debe imprimir'Robot $model is recharging'
- Una clase
main.dart: Importa tu archivo de trabajadores y demuestra ambos enfoques:- Crea un
OfficeWorkerllamado'Alice'en el departamento'Marketing' - Crea un
RobotWorkercon el modelo'RX-7' - Para el trabajador de oficina, llama a
work()y luego atakeBreak() - Imprime una línea vacía
- Para el robot trabajador, llama a
work()y luego atakeBreak()
- Crea un
Observa cómo OfficeWorker hereda takeBreak() automáticamente porque extiende Worker; realmente "es un" trabajador. Mientras tanto, RobotWorker debe proporcionar su propia implementación para todo porque solo implementa la interfaz; "puede hacer" lo que hace un trabajador, pero a su propia manera robótica.
Salida esperada:
Alice is typing reports in Marketing
Alice takes a coffee break
Robot RX-7 is assembling parts
Robot RX-7 is rechargingHoja de referencia
Usa extends para heredar el comportamiento de una clase padre. La subclase obtiene automáticamente todos los métodos del padre y puede sobrescribirlos:
class Animal {
void breathe() {
print('Breathing...');
}
}
class Dog extends Animal {
void bark() {
print('Woof!');
}
}
void main() {
var dog = Dog();
dog.breathe(); // Heredado automáticamente
dog.bark();
}Usa implements para garantizar una estructura sin heredar código. Debes proporcionar tu propia implementación para cada método:
class Robot implements Animal {
@override
void breathe() {
print('Ventilating circuits...');
}
}Diferencias clave:
extends: "Soy un tipo de esto" - herencia verdadera con código compartidoimplements: "Puedo hacer lo que esto hace" - impone un contrato con lógica personalizada- Solo puedes extender una clase, pero puedes implementar múltiples interfaces
Elige extends para relaciones verdaderas de padre e hijo donde el comportamiento deba ser heredado. Elige implements cuando necesites flexibilidad o quieras imponer un contrato sin compartir la implementación.
Pruébalo tú mismo
import 'worker.dart';
void main() {
// TODO: Crear un OfficeWorker llamado 'Alice' en el departamento de 'Marketing'
// TODO: Crear un RobotWorker con el modelo 'RX-7'
// TODO: Para el trabajador de oficina, llamar a work() y luego a takeBreak()
// TODO: Imprimir una línea vacía
// TODO: Para el trabajador robot, llamar a work() y luego a takeBreak()
}
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