Interfaces implicites
Fait partie de la section Programmation Orientée Objet du Journey Dart de Coddy — leçon 46 sur 110.
Puisque chaque classe Dart définit automatiquement une interface, vous pouvez utiliser n'importe quelle classe comme interface - même des classes concrètes avec des implémentations complètes. C'est ce que nous appelons une interface implicite.
Considérez une classe avec des propriétés et des méthodes :
class Logger {
String prefix = '[LOG]';
void log(String message) {
print('$prefix $message');
}
void clear() {
print('Clearing logs...');
}
}Lorsqu'une autre classe implémente Logger, elle doit fournir sa propre implémentation pour tout - y compris la propriété 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...');
}
}C'est l'idée fondamentale : lorsque vous utilisez implements, vous dites « ma classe aura la même forme » — les mêmes méthodes et propriétés. Vous n'héritez d'aucun code, constructeur ou valeur par défaut. Vous promettez seulement de respecter la structure de l'interface.
Cela rend les interfaces implicites puissantes pour créer des composants interchangeables. Le code qui attend un Logger peut fonctionner avec n'importe quelle classe qui implémente son interface, quelle que soit la manière dont cette implémentation fonctionne en interne.
Défi
FacileConstruisons un système de notification qui démontre comment les classes concrètes peuvent servir d'interfaces implicites. Vous allez créer une classe de notification de base, puis l'implémenter de manière complètement différente, montrant que implements vous oblige à fournir vous-même tous les membres.
Vous organiserez votre code en deux fichiers :
notification.dart: Définissez vos classes de notification ici :- Une classe
Notificationavec une propriétéString channeldéfinie sur'default', une méthodesend(String message)qui affiche[channel]: [message], et une méthodegetStatus()qui retourne la chaîne'Notification ready' - Une classe
EmailNotificationqui implémenteNotification. Puisque vous utilisezimplements, vous devez fournir votre propre propriétéchannel(définissez-la sur'email'), votre propre méthodesend()qui afficheSending email: [message], et votre propre méthodegetStatus()qui retourne'Email service active' - Une classe
PushNotificationqui implémenteNotification. Définissez sonchannelsur'push', faites en sorte quesend()affichePush alert: [message], et quegetStatus()retourne'Push service connected'
- Une classe
main.dart: Importez votre fichier de notification et démontrez comment différentes classes peuvent implémenter la même interface implicite avec des comportements complètement différents :- Créez une instance de
EmailNotificationet une dePushNotification - Pour chaque notification, affichez son statut en utilisant
getStatus(), puis appelezsend()avec le message'Hello World' - Affichez une ligne vide entre les deux notifications
- Créez une instance de
Rappel : lorsque vous utilisez implements, vous n'héritez d'aucun code de Notification. Vous promettez seulement que votre classe aura la même forme - les mêmes propriétés et méthodes. Chaque classe d'implémentation fournit sa propre implémentation complète.
Résultat attendu :
Email service active
Sending email: Hello World
Push service connected
Push alert: Hello WorldAide-mémoire
En Dart, chaque classe définit automatiquement une interface implicite. Cela signifie que vous pouvez utiliser n'importe quelle classe comme une interface, même les classes concrètes avec des implémentations complètes.
Lorsqu'une classe utilise implements, elle doit fournir sa propre implémentation pour toutes les propriétés et méthodes de l'interface. Vous n'héritez d'aucun code, constructeur ou valeur par défaut - vous promettez seulement de respecter la structure de l'interface.
Exemple d'une classe concrète :
class Logger {
String prefix = '[LOG]';
void log(String message) {
print('$prefix $message');
}
void clear() {
print('Clearing logs...');
}
}Implémentation de l'interface implicite :
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...');
}
}Cela rend les interfaces implicites puissantes pour créer des composants interchangeables. Le code qui attend un Logger peut fonctionner avec n'importe quelle classe qui implémente son interface, quelle que soit l'implémentation interne.
Essayez vous-même
import 'notification.dart';
void main() {
// TODO: Créer une instance de EmailNotification
// TODO: Afficher le statut de la notification par e-mail en utilisant getStatus()
// TODO: Envoyer un message 'Hello World' en utilisant la notification par e-mail
// TODO: Afficher une ligne vide
// TODO: Créer une instance de PushNotification
// TODO: Afficher le statut de la notification push en utilisant getStatus()
// TODO: Envoyer un message 'Hello World' en utilisant la notification push
}
Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Programmation Orientée Objet
1Les bases de la POO
Fichiers externesBibliothèques et importsIntroduction à la POOClasses vs ObjetsLe mot-clé thisMéthodesVariables d'instanceBases des constructeursRécapitulatif - Calculatrice simple4Null Safety
Introduction à la Null SafetyNullable vs Non-NullableLes opérateurs ? et !Le mot-clé late et la Null SafetyLes opérateurs Null-AwareLa Null Safety dans les classesRécapitulatif - Système de profil utilisateur7Classes abstraites et Interfaces
Classes abstraitesMéthodes abstraitesInterfaces en DartInterfaces implicitesImplémentation vs ExtensionInterfaces multiplesRécapitulatif - Calculateur de formes10Collections et Génériques
Aperçu des List, Set et MapCollections Type-SafeClasses génériquesMéthodes génériquesContraintes de généricitéIterable & IteratorRécapitulatif - Stockage générique2Les constructeurs en Dart
Constructeur par défautConstructeurs nommésListes d'initialisationConstructeurs constantsConstructeurs factoryConstructeurs de redirectionRécapitulatif - Shape Builder5Encapsulation
Membres publics vs privésLa convention du préfixe _Visibilité au niveau de la bibliothèqueApprofondissement des Getters & SettersMasquage d'informationsRécapitulatif - Dossiers d'étudiants8Mixins
Introduction aux MixinsCréation de MixinsUtilisation de plusieurs MixinsLe mot-clé on dans les MixinsMixin vs HéritageMixin vs InterfaceRécapitulatif - Système Animal11Méthodes spéciales
Redéfinition de toString()Redéfinition de hashCode & ==Interface ComparableMéthode call()Redéfinition de noSuchMethodRécapitulatif - Collection personnalisée14Patrons de conception Partie 1
Introduction aux patrons de conceptionPatron SingletonPatron FabriquePatron ObservateurPatron Stratégie3Propriétés de classe
Membres d'instance vs statiquesChamps Final & ConstVariables LateMéthodes et champs statiquesGetters et SettersRécapitulatif - Gestionnaire de compte bancaire6Héritage
Bases de l'héritageLe mot-clé superRedéfinition de méthodeL'annotation @overrideLe mot-clé final pour les classesConstructeurs et héritageRécapitulatif - Hiérarchie des employés9Polymorphisme
Bases du polymorphismePolymorphisme via les interfacesVérification de type à l'exécutionLes opérateurs is & asMot-clé covariantRécapitulatif - Processeur de paiement12POO Asynchrone
Futures & async/awaitBases des StreamsStream ControllersConstructeurs asynchronesL'asynchrone dans les méthodes de classeRécapitulatif - Data Fetcher15Patrons de conception Partie 2
Patron CommandePatron AdaptateurPatron DécorateurPatron Méthode ModèlePatron ÉtatPatron CompositePatron Repository