Singleton
Fa parte della sezione Programmazione orientata agli oggetti del percorso Dart di Coddy. Lezione 90 di 110.
Il pattern Singleton garantisce che una classe abbia una sola istanza in tutta l'applicazione e fornisce un punto di accesso globale a essa. È utile quando serve esattamente un oggetto per coordinare le operazioni in tutto il sistema, come un gestore della configurazione, una connessione al database o un servizio di logging.
In Dart, puoi implementare un Singleton usando un costruttore factory combinato con un'istanza statica privata:
class AppConfig {
static final AppConfig _instance = AppConfig._internal();
String appName = 'MyApp';
String version = '1.0.0';
// Costruttore con nome privato
AppConfig._internal();
// Il costruttore factory restituisce la stessa istanza
factory AppConfig() {
return _instance;
}
}
void main() {
var config1 = AppConfig();
var config2 = AppConfig();
config1.appName = 'UpdatedApp';
print(config2.appName); // UpdatedApp
print(identical(config1, config2)); // true
}Il costruttore privato _internal() impedisce l’istanziazione esterna. Il campo statico _instance contiene l’unica istanza, creata una sola volta quando viene caricata la classe. Ogni chiamata a AppConfig() restituisce questa stessa istanza, quindi le modifiche apportate tramite un riferimento sono visibili ovunque.
Usa i Singleton con moderazione: introducono uno stato globale, che può rendere più difficile il testing. Sono più indicati per risorse realmente condivise che necessitano di uno stato coerente in tutta l'applicazione.
Sfida
FacileCostruiamo un gestore delle impostazioni di un gioco usando il pattern Singleton! Creerai un sistema che garantisce l'esistenza di una sola istanza delle impostazioni del gioco in tutta l'applicazione, così tutte le parti del gioco condividono la stessa configurazione.
Organizzerai il codice in due file:
game_settings.dart: crea una classeGameSettingsche implementa il pattern Singleton. Il gestore delle impostazioni dovrà avere:- Un'istanza privata, statica e final che contiene l'unico oggetto
GameSettings - Un costruttore denominato privato per impedire l'istanziazione esterna
- Un costruttore factory che restituisce sempre la stessa istanza
- Tre campi per le impostazioni:
difficulty(String, valore predefinito: "Normal"),soundEnabled(bool, valore predefinito: true) eplayerName(String, valore predefinito: "Player1") - Un metodo
displaySettings()che stampa le impostazioni correnti in questo formato:Settings: [playerName] | Difficulty: [difficulty] | Sound: [on/off](stampa "on" se soundEnabled è true, "off" se è false)
- Un'istanza privata, statica e final che contiene l'unico oggetto
main.dart: importa le impostazioni del gioco e dimostra che il Singleton funziona correttamente:- Ottieni l'istanza delle impostazioni e chiamala
settings1 - Chiama
displaySettings()per mostrare i valori predefiniti - Modifica le impostazioni: cambia
playerNamein "Hero",difficultyin "Hard" esoundEnabledin false - Ottieni un altro riferimento alle impostazioni e chiamalo
settings2 - Chiama
displaySettings()susettings2per dimostrare che riflette le modifiche apportate tramitesettings1 - Stampa se
settings1esettings2sono identici usando:Same instance: [true/false]
- Ottieni l'istanza delle impostazioni e chiamala
Questo dimostra il vantaggio principale dei Singleton: le modifiche apportate in qualsiasi punto dell'applicazione sono immediatamente visibili in ogni altra parte, perché esiste una sola istanza!
Output previsto:
Settings: Player1 | Difficulty: Normal | Sound: on
Settings: Hero | Difficulty: Hard | Sound: off
Same instance: trueProvalo tu
import 'game_settings.dart';
void main() {
// TODO: Ottieni l'istanza delle impostazioni e chiamala settings1
// TODO: Chiama displaySettings() per mostrare i valori predefiniti
// TODO: Modifica le impostazioni:
// - Cambia playerName in "Hero"
// - Cambia difficulty in "Hard"
// - Cambia soundEnabled in false
// TODO: Ottieni un altro riferimento alle impostazioni e chiamalo settings2
// TODO: Chiama displaySettings() su settings2 per dimostrare che riflette le modifiche
// TODO: Stampa se settings1 e settings2 sono identici
// Formato: Same instance: [true/false]
}
Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.
Tutte le lezioni di Programmazione orientata agli oggetti
1Fondamenti della programmazione orientata agli oggetti
File esterniLibrerie e importazioniIntroduzione alla programmazione orientata agli oggettiClassi e oggettiLa parola chiave thisMetodiVariabili di istanzaFondamenti dei costruttoriRiepilogo - Calcolatrice semplice4Sicurezza dei valori null
Introduzione alla sicurezza dei valori nullNullable e non nullableGli operatori ? e !La keyword late e la sicurezza dei valori nullOperatori null-awareSicurezza dei valori null nelle classiRiepilogo - Sistema di profili utente7Classi astratte e interfacce
Classi astratteMetodi astrattiInterfacce in DartInterfacce impliciteImplementare vs estendereInterfacce multipleRiepilogo - Calcolatore di forme10Collezioni e generici
Panoramica di List, Set e MapCollezioni sicure rispetto ai tipiClassi genericheMetodi genericiVincoli sui genericiIterable e IteratorRiepilogo - Archiviazione generica13Concetti avanzati di OOP
Composizione vs ereditarietàMetodi di estensioneClassi chiamabiliClassi sealed (Dart 3)Record (Dart 3)Pattern e matching (3.0)Enum con metodi16Progetto: Gestione della biblioteca
Panoramica del progettoClassi Libro e Utente2I costruttori in Dart
Costruttore predefinitoCostruttori con nomeListe di inizializzazioneCostruttori costantiCostruttori factoryCostruttori di reindirizzamentoRiepilogo - Crea forme5Incapsulamento
Membri pubblici e privatiConvenzione del prefisso _Visibilità a livello di libreriaApprofondimento su getter e setterNascondimento delle informazioniRiepilogo - Registri degli studenti8Mixin
Introduzione ai MixinCreare MixinUsare più MixinParola chiave on nei MixinMixin vs ereditarietàMixin vs interfacciaRiepilogo - Sistema animale11Metodi speciali
Sovrascrittura di toString()Sovrascrittura di hashCode e ==Interfaccia ComparableMetodo call()Sovrascrittura di noSuchMethodRiepilogo - Collezione personalizzata14Modelli di progettazione Parte 1
Introduzione ai modelli di progettazioneSingletonFactoryObserverStrategy3Proprietà delle classi
Membri di istanza e staticiCampi final e constVariabili lateMetodi e campi staticiGetter e setterRiepilogo - Gestore di conti bancari6Ereditarietà
Ereditarietà di baseLa parola chiave superOverride dei metodiL’annotazione @overrideLa parola chiave final per le classiCostruttori ed ereditarietàRiepilogo - Gerarchia dei dipendenti9Polimorfismo
Fondamenti di polimorfismoPolimorfismo tramite interfacceControllo del tipo a runtimeGli operatori is e asParola chiave covariantRiepilogo - Elaboratore di pagamenti12OOP asincrona
Future e async/awaitNozioni di base sugli streamController degli streamCostruttori asincroniAsync nei metodi di classeRiepilogo - Recupero dei dati15Pattern di progettazione Parte 2
Pattern CommandPattern AdapterPattern DecoratorPattern Template MethodPattern StatePattern CompositePattern RepositoryEsercitati da solo: Compilatore Dart online