Mixin ou 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 vous engagez à fournir certaines méthodes, mais vous écrivez vous-même tout le code. Lorsque vous utilisez un mixin, vous obtenez gratuitement le code réel.
// 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 faut-il utiliser chacun d’eux ? Utilisez une interface lorsque différentes classes ont besoin des mêmes signatures de méthodes, mais de différentes implémentations. 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 | Incluse |
| Mot-clé | implements | with |
| Idéal pour | Des comportements différents, une 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 notifications qui montre quand utiliser des interfaces plutôt que des mixins. Vous allez créer une interface pour les notifications qui nécessitent différentes implémentations, ainsi qu’un mixin pour le comportement partagé de journalisation qui doit être identique pour tous les types de notifications.
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 renvoie unString. Les différents types de notifications implémenteront ces méthodes différemment selon leur canal spécifique.logging.dart: créez un mixinLoggingqui fournit le comportement partagé que toutes les notifications doivent 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 notifications, ce qui en fait un cas idéal pour un mixin.main.dart: importez les deux fichiers et créez deux classes de notifications qui combinent l’interface avec le mixin :- Une classe
EmailNotificationqui utilise le mixinLogginget implémenteNotifiable. Sa méthodegetChannel()doit renvoyer'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. Sa méthodegetChannel()doit renvoyer'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 unSMSNotificationet appelezsend('Quick update')- Une classe
Remarquez comment l’interface force 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 classes (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 recordedEssayez 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
1Fondamentaux de la POO
Fichiers externesBibliothèques et importsIntroduction à la POOClasses vs objetsLe mot-clé thisMéthodesVariables d’instanceBases des constructeursRécapitulatif - Calculatrice simple4Sécurité face aux valeurs nulles
Introduction à la sécurité face aux valeurs nullesNullable vs Non-NullableLes opérateurs ? et !Mot-clé Late et sécurité face aux valeurs nullesOpérateurs sensibles à nullSécurité face aux valeurs nulles dans les classesRécapitulatif – Système de profil utilisateur7Classes abstraites et interfaces
Classes abstraitesMéthodes abstraitesInterfaces en DartInterfaces implicitesImplémenter ou étendreInterfaces multiplesRécapitulatif - Calculateur de formes10Collections et génériques
Vue d’ensemble des List, Set et MapCollections à typage sûrClasses génériquesMéthodes génériquesContraintes génériquesIterable et IteratorRécapitulatif – stockage générique2Constructeurs en Dart
Constructeur par défautConstructeurs nommésListes d'initialisationConstructeurs constantsConstructeurs factoryConstructeurs redirigésRécapitulatif - Constructeur de formes5Encapsulation
Membres publics vs privésLa convention du préfixe _Confidentialité au niveau de la bibliothèqueApprofondissement des getters et settersMasquage de l’informationRécapitulatif - Fiches des étudiants8Mixins
Introduction aux MixinsCréer des MixinsUtiliser plusieurs MixinsMot-clé on dans les MixinsMixin ou héritageMixin ou interfaceRécapitulatif - Système animal11Méthodes spéciales
Redéfinition de toString()Redéfinition de hashCode et ==Interface ComparableMéthode call()Redéfinition de noSuchMethodRécapitulatif - Collection personnalisée14Patrons de conception, partie 1
Introduction aux patrons de conceptionPatron SingletonPatron FactoryPatron ObserverPatron Strategy3Propriétés des classes
Membres d’instance et statiquesChamps Final et ConstVariables différéesMéthodes et champs statiquesAccesseurs et mutateursRécapitulatif - Gestionnaire de compte bancaire6Héritage
Héritage de baseLe mot-clé superRedéfinition de méthodeL’annotation @overrideLe mot-clé de classe finalConstructeurs et héritageRécapitulatif – Hiérarchie des employés9Polymorphisme
Bases du polymorphismePolymorphisme via les interfacesVérification des types à l’exécutionLes opérateurs is et asMot-clé covariantRécapitulatif - Processeur de paiements12POO asynchrone
Futures et async/awaitNotions de base des StreamsContrôleurs de StreamsConstructeurs asynchronesAsynchrone dans les méthodes de classeRécapitulatif - récupérateur de données15Patrons de conception – Partie 2
Patron CommandePatron AdaptateurPatron DécorateurPatron Méthode modèlePatron ÉtatPatron CompositePatron DépôtEntraînez-vous par vous-même : Compilateur Dart en ligne