Entwurfsmuster in TypeScript: Von der Theorie zur Praxis
Entwurfsmuster (Design Patterns) sind bewährte Lösungen für typische Probleme in der Softwareentwicklung. Im Kontext von TypeScript, mit seinem leistungsstarken Typsystem und der OOP-Unterstützung, erhalten viele klassische Muster aus dem Buch "Gang of Four" (GoF) eine neue, elegantere Umsetzung. In diesem Artikel analysieren wir drei Schlüsselmuster mit realen Beispielen in TypeScript, die Ihnen helfen, saubereren, skalierbareren und wartbareren Code zu schreiben.
1. Erzeugungsmuster: Singleton (Einzelstück)
Zweck: Stellt sicher, dass eine Klasse nur eine Instanz hat, und bietet einen globalen Zugriffspunkt darauf.
In TypeScript ist die Singleton-Implementierung dank statischer Eigenschaften und privater Konstruktoren besonders praktisch. Dieses Muster ist ideal für die Verwaltung von Datenbankverbindungen, Loggern oder Caches.
class DatabaseConnection { private static instance: DatabaseConnection; private connectionString: string;
private constructor(connectionString: string) { this.connectionString = connectionString; console.log(`Verbindung erstellt zu: ${connectionString}`); }
public static getInstance(connectionString?: string): DatabaseConnection { if (!DatabaseConnection.instance) { if (!connectionString) { throw new Error("Connection string required for first initialization"); } DatabaseConnection.instance = new DatabaseConnection(connectionString); } return DatabaseConnection.instance; }
public query(sql: string): void { console.log(`Ausführen der Abfrage: ${sql} auf ${this.connectionString}`); }}
// Verwendungconst db1 = DatabaseConnection.getInstance("postgres://localhost:5432/mydb");const db2 = DatabaseConnection.getInstance();
console.log(db1 === db2); // true — dasselbe Objektdb1.query("SELECT * FROM users");Wichtig: In TypeScript können readonly und Generics für flexiblere Implementierungen verwendet werden. Beachten Sie jedoch, dass Singleton das Testen erschweren kann — ziehen Sie Dependency Injection (DI) als Alternative in Betracht.
2. Strukturmuster: Decorator (Dekorierer)
Zweck: Fügt einem Objekt dynamisch neue Aufgaben hinzu, ohne seine Struktur zu ändern. In TypeScript sind Dekorierer eine erstklassige Sprachfunktion (experimentell in ES5, aber aktiv in Angular und NestJS verwendet).
Betrachten wir ein Beispiel mit einem Dekorierer zur Protokollierung von Methodenaufrufen:
// Ziel-Interfaceinterface ILogger { log(message: string): void;}
// Basis-Klasseclass SimpleLogger implements ILogger { public log(message: string): void { console.log(`[LOG]: ${message}`); }}
// Dekorierer (Wrapper-Klasse)class TimestampDecorator implements ILogger { private logger: ILogger;
constructor(logger: ILogger) { this.logger = logger; }
public log(message: string): void { const timestamp = new Date().toISOString(); this.logger.log(`[${timestamp}] ${message}`); }}
// Dekorierer für Formatierungclass UpperCaseDecorator implements ILogger { private logger: ILogger;
constructor(logger: ILogger) { this.logger = logger; }
public log(message: string): void { this.logger.log(message.toUpperCase()); }}
// Verwendunglet logger: ILogger = new SimpleLogger();logger.log("Hallo Welt"); // [LOG]: Hallo Welt
logger = new TimestampDecorator(logger);logger = new UpperCaseDecorator(logger);logger.log("dekorierer funktionieren"); // [LOG]: [2025-01-15T10:30:00.000Z] DEKORIERER FUNKTIONIERENTipp: In TypeScript sind auch Klassen- und Eigenschafts-Dekorierer verfügbar (Syntax @decorator