Entwurfsmuster in TypeScript: Ein praktischer Leitfaden
Entwurfsmuster (Design Patterns) sind bewährte Lösungen für typische Aufgaben in der Softwareentwicklung. In TypeScript lassen sich viele Muster dank der strengen Typisierung und der OOP-Fähigkeiten besonders elegant und sicher umsetzen. In diesem Artikel betrachten wir vier wichtige Muster mit realen Codebeispielen, die Ihnen helfen, flexibleren und wartbareren Code zu schreiben.
1. Singleton (Einzelstück)
Das Singleton-Muster stellt sicher, dass eine Klasse nur eine einzige Instanz besitzt, und bietet einen globalen Zugriffspunkt darauf. In TypeScript ist dies besonders nützlich für die Verwaltung der Anwendungskonfiguration oder von Datenbankverbindungen.
Implementierung mit einem privaten Konstruktor und einer statischen Methode:
class DatabaseConnection { private static instance: DatabaseConnection; private connection: string;
private constructor() { this.connection = 'PostgreSQL connected'; }
public static getInstance(): DatabaseConnection { if (!DatabaseConnection.instance) { DatabaseConnection.instance = new DatabaseConnection(); } return DatabaseConnection.instance; }
public query(sql: string): void { console.log(`Executing query: ${sql} on ${this.connection}`); }}
// Verwendungconst db1 = DatabaseConnection.getInstance();const db2 = DatabaseConnection.getInstance();console.log(db1 === db2); // true - dasselbe Objektdb1.query('SELECT * FROM users');Wann verwenden: für Logger, Caches, Thread-Pools, Anwendungseinstellungen. Vermeiden Sie Singleton für Zustände, die sich in einer Multithread-Umgebung ändern können.
2. Factory Method (Fabrikmethode)
Die Factory Method definiert eine Schnittstelle zur Erstellung eines Objekts, erlaubt es Unterklassen jedoch, den Typ des erstellten Objekts zu ändern. In TypeScript harmoniert dies hervorragend mit Interfaces und abstrakten Klassen.
Beispiel für die Erstellung verschiedener Benachrichtigungstypen:
interface Notification { send(message: string): void;}
class EmailNotification implements Notification { public send(message: string): void { console.log(`Email sent: ${message}`); }}
class SMSNotification implements Notification { public send(message: string): void { console.log(`SMS sent: ${message}`); }}
abstract class NotificationFactory { public abstract createNotification(): Notification;
public notifyUser(message: string): void { const notification = this.createNotification(); notification.send(message); }}
class EmailNotificationFactory extends NotificationFactory { public createNotification(): Notification { return new EmailNotification(); }}
class SMSNotificationFactory extends NotificationFactory { public createNotification(): Notification { return new SMSNotification(); }}
// Verwendungconst factory: NotificationFactory = new EmailNotificationFactory();factory.notifyUser('Your order has been shipped!');Wann verwenden: wenn im Voraus nicht bekannt ist, welche konkrete Klasse benötigt wird, oder wenn die Logik zur Objekterstellung komplex ist und zentralisiert werden soll.
3. Observer (Beobachter)
Der Observer definiert eine "Eins-zu-Viele"-Beziehung zwischen Objekten: Wenn sich der Zustand eines Objekts ändert, werden alle abhängigen Objekte automatisch benachrichtigt. In TypeScript wird dies oft über Generics zur Typisierung von Ereignissen implementiert.
Beispiel für ein Nachrichten-Abonnementsystem:
interface Observer<T> { update(data: T): void;}
class NewsPublisher<T> { private observers: Observer<T>[] = [];
public subscribe(observer: Observer<T>): void { this.observers.push(observer); }
public unsubscribe(observer: Observer<T>): void { this.observers = this.observers.filter(obs => o```