Grundlagen 🌟
Einführung in das Singleton-Pattern
Stellen Sie sich einen einzigartigen Superhelden vor, der nur in einem einzigen Exemplar im Universum existieren kann. Genau das ist das Singleton-Pattern! 🦸♂️
Warum brauchen wir einen einzigartigen Helden? 🤔
Hier einige typische Szenarien:
- 🎮 Ein Videospiel mit mehreren Bestenlisten... totales Chaos!
- 🏦 Mehrere Verbindungen zur gleichen Datenbank... pure Ressourcenverschwendung!
- ⚙️ Mehrere verschiedene Konfigurationen... das Tor zur Unordnung!
Superkräfte des Singletons
- 💪 Kraft der Einzigartigkeit: Eine einzige Instanz, wie ein Batman für Gotham
- 🌟 Globale Sicht: Überall zugänglich, wie das Bat-Signal am Himmel
- 🎯 Präzision: Eine einzige Quelle der Wahrheit, wie der eine Ring von Sauron
Grundlegende Implementierung
Der Code unseres Helden
1class Logger {
2 private static instance: Logger;
3 private logCount: number = 0;
4
5 private constructor() {
6 console.log('🚀 Logger erwacht...');
7 }
8
9 public static getInstance(): Logger {
10 if (!Logger.instance) {
11 Logger.instance = new Logger();
12 }
13 return Logger.instance;
14 }
15
16 public log(message: string): void {
17 this.logCount++;
18 console.log(`📝 [Log #${this.logCount}] ${message}`);
19 }
20
21 public getStats(): string {
22 return `📊 Gesamte Logs: ${this.logCount}`;
23 }
24}
Einfache Verwendung
1const logger = Logger.getInstance();
2logger.log('Unser Held ist bereit!');
3console.log(logger.getStats());
Fortgeschrittene Techniken 🚀
Thread-sicheres Singleton
In einer Multi-Thread-Umgebung braucht unser Held zusätzliche Rüstung! Hier eine thread-sichere Implementierung:
1class ThreadSafeLogger {
2 private static instance: ThreadSafeLogger;
3 private static instanceLock = false;
4 private logs: string[] = [];
5
6 private constructor() {}
7
8 public static getInstance(): ThreadSafeLogger {
9 if (!ThreadSafeLogger.instanceLock) {
10 ThreadSafeLogger.instanceLock = true;
11 if (!ThreadSafeLogger.instance) {
12 ThreadSafeLogger.instance = new ThreadSafeLogger();
13 }
14 ThreadSafeLogger.instanceLock = false;
15 }
16 return ThreadSafeLogger.instance;
17 }
18}
Singleton mit Lazy Initialization 🦥
1class LazyLogger {
2 private static instance: LazyLogger;
3 private config: object;
4
5 private constructor(config: object) {
6 this.config = config;
7 }
8
9 public static getInstance(config?: object): LazyLogger {
10 if (!LazyLogger.instance && config) {
11 LazyLogger.instance = new LazyLogger(config);
12 }
13 if (!LazyLogger.instance) {
14 throw new Error(
15 'Logger benötigt Konfiguration bei der ersten Initialisierung!',
16 );
17 }
18 return LazyLogger.instance;
19 }
20}
Testbares Singleton 🧪
1class TestableLogger {
2 private static instance: TestableLogger;
3
4 private constructor() {}
5
6 public static getInstance(): TestableLogger {
7 if (!TestableLogger.instance) {
8 TestableLogger.instance = new TestableLogger();
9 }
10 return TestableLogger.instance;
11 }
12
13 // Spezielle Methode für Tests
14 public static resetInstance(): void {
15 TestableLogger.instance = null;
16 }
17}
Generisches Singleton 🎭
1class GenericSingleton<T> {
2 private static instances: Map<string, any> = new Map();
3
4 protected constructor() {}
5
6 public static getInstance<T>(this: new () => T): T {
7 const className = this.name;
8 if (!GenericSingleton.instances.has(className)) {
9 GenericSingleton.instances.set(className, new this());
10 }
11 return GenericSingleton.instances.get(className);
12 }
13}
14
15// Verwendung
16class UserService extends GenericSingleton<UserService> {
17 public getUsers() {
18 return ['user1', 'user2'];
19 }
20}
21
22class ConfigService extends GenericSingleton<ConfigService> {
23 public getConfig() {
24 return { api: 'url' };
25 }
26}
Anti-Pattern und zu vermeidende Fallstricke ⚠️
- Enge Kopplung
1// Vermeiden ❌
2class BadSingleton {
3 public static getInstance() {
4 // Code...
5 }
6 public doDirectDatabaseOperation() {
7 // Direkte DB-Operation
8 }
9}
10
11// Bevorzugen ✅
12interface DatabaseOperation {
13 execute(): void;
14}
15
16class GoodSingleton {
17 public static getInstance() {
18 // Code...
19 }
20 public executeOperation(operation: DatabaseOperation) {
21 operation.execute();
22 }
23}
- Globaler veränderbarer Zustand
1// Vermeiden ❌
2class MutableSingleton {
3 private static instance: MutableSingleton;
4 public globalState: any = {};
5}
6
7// Bevorzugen ✅
8class ImmutableSingleton {
9 private static instance: ImmutableSingleton;
10 private state: Readonly<any>;
11
12 public getState(): Readonly<any> {
13 return this.state;
14 }
15}
Ergänzende Patterns 🤝
Factory + Singleton
1interface Logger {
2 log(message: string): void;
3}
4
5class LoggerFactory {
6 private static instance: LoggerFactory;
7 private loggers: Map<string, Logger> = new Map();
8
9 private constructor() {}
10
11 public static getInstance(): LoggerFactory {
12 if (!LoggerFactory.instance) {
13 LoggerFactory.instance = new LoggerFactory();
14 }
15 return LoggerFactory.instance;
16 }
17
18 public getLogger(type: 'console' | 'file'): Logger {
19 if (!this.loggers.has(type)) {
20 this.loggers.set(type, this.createLogger(type));
21 }
22 return this.loggers.get(type);
23 }
24
25 private createLogger(type: 'console' | 'file'): Logger {
26 // Spezifischen Logger erstellen
27 return type === 'console' ? new ConsoleLogger() : new FileLogger();
28 }
29}
Fortgeschrittene Herausforderung 🎯
Erstellen Sie ein Singleton, das:
- Thread-sicher ist
- Lazy Initialization verwendet
- Reset für Tests erlaubt
- Ein Versionierungssystem integriert
Zusammenfassung 🎬
Das Singleton ist ein mächtiges Pattern, erfordert aber Überlegung bei der Implementierung. Die Basisversion reicht für einfache Fälle aus, aber fortgeschrittene Versionen bieten mehr Robustheit und Flexibilität für komplexe Anwendungen.
Profi-Tipps 💡
- Verwenden Sie Lazy Initialization für ressourcenintensive Operationen
- Denken Sie immer an die Testbarkeit
- Bevorzugen Sie Dependency Injection, wenn möglich
- Dokumentieren Sie Ihre Singleton-Anwendungsfälle gut
Praktische Übungen 🎮
- Implementieren Sie ein Singleton zur Verwaltung von Anwendungskonfigurationen
- Erstellen Sie ein thread-sicheres Singleton mit einer Nachrichtenwarteschlange
- Entwickeln Sie ein Caching-System mit dem Singleton-Pattern
Frohes Programmieren! 🚀