Patron Singleton
Fait partie de la section Programmation Orientée Objet du Journey Dart de Coddy — leçon 90 sur 110.
Le pattern Singleton garantit qu'une classe n'a qu'une seule instance dans toute votre application et fournit un point d'accès global à celle-ci. C'est utile lorsqu'un seul objet est nécessaire pour coordonner les actions à travers le système - comme un gestionnaire de configuration, une connexion à une base de données ou un service de journalisation.
En Dart, vous pouvez implémenter un Singleton en utilisant un constructeur de type factory combiné à une instance statique privée :
class AppConfig {
static final AppConfig _instance = AppConfig._internal();
String appName = 'MyApp';
String version = '1.0.0';
// Constructeur nommé privé
AppConfig._internal();
// Le constructeur factory retourne la même instance
factory AppConfig() {
return _instance;
}
}
void main() {
var config1 = AppConfig();
var config2 = AppConfig();
config1.appName = 'UpdatedApp';
print(config2.appName); // UpdatedApp
print(identical(config1, config2)); // true
}Le constructeur privé _internal() empêche l'instanciation externe. Le champ statique _instance contient l'instance unique, créée une seule fois lors du chargement de la classe. Chaque appel à AppConfig() retourne cette même instance, de sorte que les modifications effectuées via une référence sont visibles partout.
Utilisez les Singletons avec parcimonie - ils introduisent un état global, ce qui peut rendre les tests plus difficiles. Ils sont mieux adaptés aux ressources véritablement partagées qui nécessitent un état cohérent dans toute votre application.
Défi
FacileConstruisons un gestionnaire de paramètres de jeu en utilisant le motif Singleton ! Vous allez créer un système qui garantit qu'une seule instance des paramètres de jeu existe dans toute l'application, afin que toutes les parties de votre jeu partagent la même configuration.
Vous organiserez votre code en deux fichiers :
game_settings.dart: Créez une classeGameSettingsqui implémente le motif Singleton. Votre gestionnaire de paramètres doit comporter :- Une instance statique finale privée qui détient l'unique objet
GameSettings - Un constructeur nommé privé pour empêcher l'instanciation externe
- Un constructeur d'usine (factory) qui retourne toujours la même instance
- Trois champs de paramètres :
difficulty(String, par défaut : "Normal"),soundEnabled(bool, par défaut : true), etplayerName(String, par défaut : "Player1") - Une méthode
displaySettings()qui affiche les paramètres actuels sous ce format :Settings: [playerName] | Difficulty: [difficulty] | Sound: [on/off](affichez "on" si soundEnabled est true, "off" si false)
- Une instance statique finale privée qui détient l'unique objet
main.dart: Importez vos paramètres de jeu et démontrez que le Singleton fonctionne correctement :- Récupérez l'instance des paramètres et nommez-la
settings1 - Appelez
displaySettings()pour afficher les valeurs par défaut - Modifiez les paramètres : changez
playerNameen "Hero",difficultyen "Hard", etsoundEnableden false - Récupérez une autre référence aux paramètres et nommez-la
settings2 - Appelez
displaySettings()sursettings2pour prouver qu'il reflète les modifications apportées viasettings1 - Affichez si
settings1etsettings2sont identiques en utilisant :Same instance: [true/false]
- Récupérez l'instance des paramètres et nommez-la
Cela démontre l'avantage principal des Singletons : les modifications apportées n'importe où dans votre application sont immédiatement visibles partout ailleurs, car il n'y a qu'une seule instance !
Sortie attendue :
Settings: Player1 | Difficulty: Normal | Sound: on
Settings: Hero | Difficulty: Hard | Sound: off
Same instance: trueAide-mémoire
Le pattern Singleton garantit qu'une classe n'a qu'une seule instance dans toute votre application et fournit un point d'accès global à celle-ci.
En Dart, implémentez un Singleton en utilisant un constructeur factory combiné à une instance statique privée :
class AppConfig {
static final AppConfig _instance = AppConfig._internal();
String appName = 'MyApp';
String version = '1.0.0';
// Private named constructor
AppConfig._internal();
// Factory constructor returns the same instance
factory AppConfig() {
return _instance;
}
}Composants clés :
static final _instance: Détient l'instance unique, créée une seule fois lors du chargement de la classe_internal(): Le constructeur privé empêche l'instanciation externefactory AppConfig(): Retourne la même instance à chaque fois
Exemple d'utilisation :
var config1 = AppConfig();
var config2 = AppConfig();
config1.appName = 'UpdatedApp';
print(config2.appName); // UpdatedApp
print(identical(config1, config2)); // trueLes modifications effectuées via une référence sont visibles partout car toutes les références pointent vers la même instance.
Essayez vous-même
import 'game_settings.dart';
void main() {
// TODO: Récupérer l'instance de settings et l'appeler settings1
// TODO: Appeler displaySettings() pour afficher les valeurs par défaut
// TODO: Modifier les paramètres :
// - Changer playerName en "Hero"
// - Changer difficulty en "Hard"
// - Changer soundEnabled en false
// TODO: Récupérer une autre référence à settings et l'appeler settings2
// TODO: Appeler displaySettings() sur settings2 pour prouver qu'il reflète les changements
// TODO: Afficher si settings1 et settings2 sont identiques
// Format : Same instance: [true/false]
}
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