Interfaces implícitas
Parte de la sección Programación Orientada a Objetos del Journey de Dart de Coddy — lección 46 de 110.
Dado que cada clase de Dart define automáticamente una interfaz, puedes usar cualquier clase como una interfaz, incluso clases concretas con implementaciones completas. Esto es lo que llamamos una interfaz implícita.
Considera una clase con propiedades y métodos:
class Logger {
String prefix = '[LOG]';
void log(String message) {
print('$prefix $message');
}
void clear() {
print('Clearing logs...');
}
}Cuando otra clase implementa Logger, debe proporcionar su propia implementación para todo, incluyendo la propiedad prefix:
class FileLogger implements Logger {
@override
String prefix = '[FILE]';
@override
void log(String message) {
print('$prefix Writing to file: $message');
}
@override
void clear() {
print('Deleting log file...');
}
}Esta es la idea clave: cuando usas implements, estás diciendo "mi clase tendrá la misma forma" - los mismos métodos y propiedades. No heredas ningún código, constructores o valores por defecto. Solo prometes cumplir con la estructura de la interfaz.
Esto hace que las interfaces implícitas sean potentes para crear componentes intercambiables. El código que espera un Logger puede funcionar con cualquier clase que implemente su interfaz, independientemente de cómo funcione esa implementación internamente.
Desafío
FácilVamos a construir un sistema de notificaciones que demuestre cómo las clases concretas pueden servir como interfaces implícitas. Crearás una clase de notificación base y luego la implementarás de una manera completamente diferente, demostrando que implements requiere que proporciones todos los miembros tú mismo.
Organizarás tu código en dos archivos:
notification.dart: Define tus clases de notificación aquí:- Una clase
Notificationcon una propiedadString channelestablecida en'default', un métodosend(String message)que imprime[channel]: [message], y un métodogetStatus()que devuelve el string'Notification ready' - Una clase
EmailNotificationque implementaNotification. Dado que estás usandoimplements, debes proporcionar tu propia propiedadchannel(establécela en'email'), tu propio métodosend()que imprimeSending email: [message], y tu propio métodogetStatus()que devuelve'Email service active' - Una clase
PushNotificationque implementaNotification. Establece suchannelen'push', haz quesend()imprimaPush alert: [message], y haz quegetStatus()devuelva'Push service connected'
- Una clase
main.dart: Importa tu archivo de notificación y demuestra cómo diferentes clases pueden implementar la misma interfaz implícita con comportamientos completamente diferentes:- Crea una instancia de
EmailNotificationy una dePushNotification - Para cada notificación, imprime su estado usando
getStatus(), luego llama asend()con el mensaje'Hello World' - Imprime una línea vacía entre las dos notificaciones
- Crea una instancia de
Recuerda: cuando usas implements, no estás heredando ningún código de Notification. Solo estás prometiendo que tu clase tendrá la misma forma: las mismas propiedades y métodos. Cada clase implementadora proporciona su propia implementación completa.
Salida esperada:
Email service active
Sending email: Hello World
Push service connected
Push alert: Hello WorldHoja de referencia
En Dart, cada clase define automáticamente una interfaz implícita. Esto significa que puedes usar cualquier clase como una interfaz, incluso clases concretas con implementaciones completas.
Cuando una clase usa implements, debe proporcionar su propia implementación para todas las propiedades y métodos de la interfaz. No heredas ningún código, constructores o valores por defecto; solo prometes cumplir con la estructura de la interfaz.
Ejemplo de una clase concreta:
class Logger {
String prefix = '[LOG]';
void log(String message) {
print('$prefix $message');
}
void clear() {
print('Clearing logs...');
}
}Implementando la interfaz implícita:
class FileLogger implements Logger {
@override
String prefix = '[FILE]';
@override
void log(String message) {
print('$prefix Writing to file: $message');
}
@override
void clear() {
print('Deleting log file...');
}
}Esto hace que las interfaces implícitas sean potentes para crear componentes intercambiables. El código que espera un Logger puede funcionar con cualquier clase que implemente su interfaz, independientemente de la implementación interna.
Pruébalo tú mismo
import 'notification.dart';
void main() {
// TODO: Crear una instancia de EmailNotification
// TODO: Imprimir el estado de la notificación por correo electrónico usando getStatus()
// TODO: Enviar un mensaje 'Hello World' usando la notificación por correo electrónico
// TODO: Imprimir una línea vacía
// TODO: Crear una instancia de PushNotification
// TODO: Imprimir el estado de la notificación push usando getStatus()
// TODO: Enviar un mensaje 'Hello World' usando la notificación push
}
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