Das Singleton-Muster ist ein Entwurfsmuster, das dazu dient, sicherzustellen, dass eine Klasse nur eine einzige Instanz hat und der Zugriff auf diese Instanz global erfolgt. Es ist besonders nützlich, wenn Sie sicherstellen möchten, dass eine bestimmte Klasse nur einmal in einer Anwendung erstellt wird, und dann von überall aus darauf zugegriffen werden kann.
Ein Singleton kann auf verschiedene Weise implementiert werden, aber im Wesentlichen besteht es aus einer Klasse mit einer privaten Konstruktorfunktion und einer statischen Eigenschaft, die die einzige Instanz der Klasse speichert. Der Zugriff auf die Instanz erfolgt über eine statische Methode der Klasse, die sicherstellt, dass immer nur eine Instanz erstellt wird.
Das Singleton-Muster hat mehrere Vorteile:
- Es stellt sicher, dass eine Klasse nur einmal erstellt wird, was Speicherplatz spart und mögliche Fehler vermeidet, die durch das Erstellen mehrerer Instanzen der gleichen Klasse entstehen können.
- Es erleichtert die Verwaltung von globalen Ressourcen (z.B. einer Datenbankverbindung), die von verschiedenen Teilen einer Anwendung verwendet werden.
- Es erleichtert die Implementierung von Frameworks und Bibliotheken, indem es sicherstellt, dass bestimmte Klassen nur einmal erstellt werden und dann von verschiedenen Teilen der Anwendung verwendet werden können.
Wenn Sie eine Anwendung haben, die häufig auf eine Datenbank zugreift, kann das Erstellen einer Verbindung zur Datenbank jedes Mal, wenn Sie auf die Datenbank zugreifen müssen, eine erhebliche Belastung für die Ressourcen Ihrer Anwendung darstellen. Durch die Verwendung des Singleton-Musters können Sie sicherstellen, dass nur eine Verbindung erstellt wird und von verschiedenen Teilen Ihrer Anwendung verwendet werden kann.
Beispiel
class MySingleton {
private static instance: MySingleton;
private constructor() {}
public static getInstance(): MySingleton {
if (!MySingleton.instance) {
MySingleton.instance = new MySingleton();
}
return MySingleton.instance;
}
public doSomething(): void {
console.log("Singleton instance is doing something.");
}
}
const mySingleton = MySingleton.getInstance();
mySingleton.doSomething();
// Output: Singleton instance is doing something.
In diesem Beispiel haben wir eine Klasse MySingleton
definiert, die einen privaten Konstruktor hat und eine statische Eigenschaft instance
, die die einzige Instanz der Klasse speichert. Die statische Methode getInstance
stellt sicher, dass nur eine Instanz erstellt wird, indem sie überprüft, ob instance
bereits initialisiert wurde, bevor sie eine neue Instanz erstellt. Schließlich haben wir eine Methode doSomething
, die von der Singleton-Instanz aufgerufen werden kann.
Um auf die Singleton-Instanz zuzugreifen, rufen wir einfach MySingleton.getInstance()
auf. Wenn es bereits eine Instanz gibt, wird diese zurückgegeben. Andernfalls wird eine neue Instanz erstellt und zurückgegeben.
Anwendungsfall: Datenbankverbindung
Wir isolieren unser Singleton, welches die Datenbankverbindung bereithält, mittels eines weiteren Musters: Factory
.
Die Factory-Methode ist ein Entwurfsmuster, das dazu dient, die Erstellung von Objekten zu kapseln und zu isolieren. Es ermöglicht die Flexibilität, die Art und Weise zu ändern, wie ein Objekt erstellt wird, ohne den Code zu ändern, der das Objekt verwendet.
In Bezug auf das Singleton-Muster könnte die Factory-Methode verwendet werden, um die Erstellung der Singleton-Instanz in einer separaten Klasse zu kapseln und zu isolieren. Auf diese Weise wird sichergestellt, dass nur die Factory-Klasse die Singleton-Instanz erstellt und dass sie nicht von anderen Teilen der Anwendung direkt erstellt werden kann.
Durch die Verwendung der Factory-Methode können auch weitere Vorteile erzielt werden, wie beispielsweise eine bessere Testbarkeit des Codes, da es einfacher ist, Mock-Objekte für die Factory-Klasse zu erstellen und zu verwenden, um die Singleton-Instanz zu simulieren.
class DatabaseConnection {
private static instance: DatabaseConnection;
private constructor() {
// Set up database connection
}
static getInstance(): DatabaseConnection {
if (!DatabaseConnection.instance) {
DatabaseConnection.instance = new DatabaseConnection();
}
return DatabaseConnection.instance;
}
// Database operations
// ...
}
class DatabaseConnectionFactory {
static createConnection(): DatabaseConnection {
return DatabaseConnection.getInstance();
}
}
// Usage:
const dbConnection = DatabaseConnectionFactory.createConnection();
// Use dbConnection for database operations
In diesem Beispiel erstellen wir eine Klasse namens DatabaseConnection
, die eine Datenbankverbindung darstellt. Die getInstance()
-Methode der Klasse stellt sicher, dass nur eine einzige Instanz der DatabaseConnection
-Klasse erstellt wird.
Die DatabaseConnectionFactory
-Klasse wird verwendet, um eine Verbindung zur Datenbank zu erstellen. Sie kapselt die Erstellung der DatabaseConnection
-Instanz, so dass nur die Factory-Klasse die Instanz erstellen kann.
Durch die Verwendung der Factory-Methode können wir sicherstellen, dass die Erstellung der DatabaseConnection
-Instanz gekapselt ist und von anderen Teilen der Anwendung nicht direkt aufgerufen werden kann. Außerdem können wir bei Bedarf eine andere Factory-Klasse erstellen, um eine andere Art von Verbindung zur Datenbank zu erstellen.
Tests
Um einen Mock der Datenbankverbindung zu erstellen, können Sie eine neue Klasse erstellen, die das gleiche Interface wie die DatabaseConnection
-Klasse hat, aber eine andere Implementierung verwendet, die keine tatsächliche Verbindung zur Datenbank herstellt. Hier ist ein Beispielcode:
interface IDatabaseConnection {
// Database operations
// ...
}
class DatabaseConnection implements IDatabaseConnection {
private static instance: DatabaseConnection;
private constructor() {
// Set up database connection
}
static getInstance(): IDatabaseConnection {
if (!DatabaseConnection.instance) {
DatabaseConnection.instance = new DatabaseConnection();
}
return DatabaseConnection.instance;
}
// Database operations
// ...
}
class MockDatabaseConnection implements IDatabaseConnection {
// Mock implementation for database operations
// ...
}
class DatabaseConnectionFactory {
static createConnection(): IDatabaseConnection {
if (process.env.NODE_ENV === 'test') {
return new MockDatabaseConnection();
}
return DatabaseConnection.getInstance();
}
}
// Usage:
const dbConnection = DatabaseConnectionFactory.createConnection();
// Use dbConnection for database operations
In diesem Beispiel haben wir eine neue Schnittstelle namens IDatabaseConnection
definiert, die die Methoden definiert, die von der Datenbankverbindung verwendet werden. Die DatabaseConnection
-Klasse implementiert diese Schnittstelle und stellt die tatsächliche Verbindung zur Datenbank her.
Die MockDatabaseConnection
-Klasse implementiert ebenfalls die IDatabaseConnection
-Schnittstelle, verwendet jedoch eine Mock-Implementierung für die Methoden, die keine tatsächliche Verbindung zur Datenbank herstellen.
In der DatabaseConnectionFactory
-Klasse haben wir eine Bedingung hinzugefügt, die prüft, ob sich die Anwendung im Testmodus befindet. Wenn dies der Fall ist, wird eine MockDatabaseConnection
-Instanz erstellt, andernfalls wird die DatabaseConnection
-Instanz erstellt.
Durch die Verwendung von process.env.NODE_ENV
können wir die Umgebung der Anwendung bestimmen. Wenn der Testmodus aktiviert ist, können wir eine Mock-Implementierung verwenden, um Tests auszuführen, ohne tatsächlich auf die Datenbank zugreifen zu müssen.