Mixin vs Interface
Fait partie de la section Programmation Orientée Objet du Journey Dart de Coddy — leçon 55 sur 110.
Vous avez vu comment les mixins se comparent à l'héritage. Comparons maintenant les mixins aux interfaces, car les deux vous permettent de travailler avec plusieurs types. La différence clé réside dans ce qu'elles fournissent : les interfaces définissent un contrat, tandis que les mixins fournissent une implémentation.
Lorsque vous implémentez une interface, vous promettez de fournir certaines méthodes, mais vous écrivez tout le code vous-même. Lorsque vous utilisez un mixin, vous obtenez le code réel gratuitement.
// Approche par interface - vous devez tout implémenter
abstract class Printable {
void printInfo();
}
class Report implements Printable {
@override
void printInfo() {
print('Printing report...'); // Vous écrivez ceci
}
}
// Approche par mixin - implémentation incluse
mixin Printable {
void printInfo() {
print('Printing...'); // Déjà écrit pour vous
}
}
class Report with Printable {} // Obtient printInfo() automatiquementAlors, quand devriez-vous utiliser l'un ou l'autre ? Utilisez une interface lorsque différentes classes ont besoin des mêmes signatures de méthode mais d'implémentations différentes. Utilisez un mixin lorsque les classes doivent partager exactement le même comportement.
| Fonctionnalité | Interface | Mixin |
|---|---|---|
| Fournit | Contrat uniquement | Contrat + code |
| Implémentation | Vous l'écrivez | Inclus |
| Mot-clé | implements | with |
| Idéal pour | Comportements différents, même API | Comportement partagé |
Vous pouvez même combiner les deux approches : utiliser des interfaces pour définir ce qu'une classe peut faire, et des mixins pour fournir des implémentations communes le cas échéant. En Dart, lors de la combinaison des deux, placez toujours with avant implements :
class MyClass with SomeMixin implements SomeInterface {
// with se place avant implements
}Défi
FacileConstruisons un système de notification qui démontre quand utiliser des interfaces par rapport aux mixins. Vous allez créer une interface pour les notifications qui nécessitent des implémentations différentes, et un mixin pour un comportement de journalisation partagé qui doit être identique pour tous les types de notification.
Vous organiserez votre code en trois fichiers :
notifiable.dart: Définissez une classe abstraiteNotifiablequi agit comme une interface. Elle doit déclarer deux méthodes abstraites :send(String message)etgetChannel()qui retourne uneString. Différents types de notification implémenteront ces méthodes différemment selon leur canal spécifique.logging.dart: Créez un mixinLoggingqui fournit un comportement partagé que toutes les notifications devraient avoir. Incluez une méthodelogSent(String channel, String message)qui affiche[LOG] Sent via [channel]: [message]et une méthodelogTimestamp()qui affiche[LOG] Timestamp recorded. Ce comportement est identique pour tous les types de notification, ce qui le rend parfait pour un mixin.main.dart: Importez les deux fichiers et créez deux classes de notification qui combinent l'interface avec le mixin :- Une classe
EmailNotificationqui utilise le mixinLogginget implémenteNotifiable. SongetChannel()doit retourner'Email', et sa méthodesend()doit afficherSending email: [message], puis appelerlogSent()avec le canal et le message, puis appelerlogTimestamp() - Une classe
SMSNotificationqui utilise le mixinLogginget implémenteNotifiable. SongetChannel()doit retourner'SMS', et sa méthodesend()doit afficherSending SMS: [message], puis appelerlogSent()avec le canal et le message, puis appelerlogTimestamp()
EmailNotificationet appelezsend('Hello World'), affichez une ligne vide, créez uneSMSNotificationet appelezsend('Quick update')- Une classe
Remarquez comment l'interface oblige chaque classe à écrire ses propres implémentations de send() et getChannel() (comportement différent), tandis que le mixin fournit les méthodes de journalisation qui fonctionnent de manière identique pour les deux (comportement partagé).
Sortie attendue :
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 recordedAide-mémoire
Les interfaces définissent un contrat (signatures de méthodes), tandis que les mixins fournissent une implémentation (code réel).
Lorsque vous implémentez une interface, vous devez écrire tout le code vous-même :
abstract class Printable {
void printInfo();
}
class Report implements Printable {
@override
void printInfo() {
print('Printing report...'); // Vous écrivez ceci
}
}Lorsque vous utilisez un mixin, vous obtenez l'implémentation gratuitement :
mixin Printable {
void printInfo() {
print('Printing...'); // Déjà écrit pour vous
}
}
class Report with Printable {} // Obtient printInfo() automatiquementQuand utiliser chacun :
- Utilisez une interface lorsque différentes classes ont besoin des mêmes signatures de méthodes mais d'implémentations différentes
- Utilisez un mixin lorsque les classes doivent partager exactement le même comportement
| Fonctionnalité | Interface | Mixin |
|---|---|---|
| Fournit | Contrat uniquement | Contrat + code |
| Implémentation | Vous l'écrivez | Inclus |
| Mot-clé | implements | with |
| Idéal pour | Comportements différents, même API | Comportement partagé |
Vous pouvez combiner les deux approches. Lors de la combinaison, placez toujours with avant implements :
class MyClass with SomeMixin implements SomeInterface {
// with vient avant implements
}Essayez vous-même
import 'notifiable.dart';
import 'logging.dart';
// TODO: Créer la classe EmailNotification qui :
// - utilise le mixin Logging (avec with)
// - implémente Notifiable (avec implements)
// - getChannel() retourne 'Email'
// - send() affiche 'Sending email: [message]', puis appelle logSent(), puis logTimestamp()
// TODO: Créer la classe SMSNotification qui :
// - utilise le mixin Logging (avec with)
// - implémente Notifiable (avec implements)
// - getChannel() retourne 'SMS'
// - send() affiche 'Sending SMS: [message]', puis appelle logSent(), puis logTimestamp()
void main() {
// TODO: Créer une EmailNotification et appeler send('Hello World')
// TODO: Afficher une ligne vide
// TODO: Créer une SMSNotification et appeler send('Quick update')
}
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