4D 20 R5 bietet eine leistungsstarke Funktion für Entwickler: Singletons!
Das Singleton Design Pattern erzeugt eine einzige Instanz einer Klasse, die in der gesamten Anwendung zugänglich ist.
Dieses Muster bietet viele Vorteile, darunter:
- Host für prozessübergreifende Werte,
- Hilfsklassen,
- eine Basis für das Factory Design Pattern,
- und vieles mehr.
Lesen Sie weiter, um mehr über dieses neue Konzept zu erfahren!
4D kennt 2 Arten von Singletons: Singletons, die pro Prozess eindeutig sind und Shared Singletons, die in der gesamten Anwendung eindeutig sind. Singletons sind Standardobjekte, während Shared Singletons gemeinsam genutzte Objekte sind.
Die Definition eines Singletons ist sehr einfach: Sie müssen lediglich das Schlüsselwort singleton in einen Klassenkonstruktor einfügen:
singleton Class constructor()
Die Definition eines gemeinsam genutzten Singletons ist ebenfalls sehr einfach: Sie müssen nur das Schlüsselwort singleton in einer gemeinsam genutzten Klasse verwenden:
shared singleton Class constructor()
Von diesem Moment an können Sie über das me-Attribut einfach auf Ihre Singletons zugreifen:
$singleton:=cs.SingletonClass.me
Das me-Attribut ist die Singleton-Instanz (auf die in anderen Sprachen oft über die Funktion getInstance() zugegriffen wird). Es wird instanziiert, wenn Sie das erste Mal auf Ihr Singleton zugreifen.
Beispiele
Singletons sind sehr nützlich, und wir erwarten, dass Sie sie häufig verwenden werden. Ich denke, Sie werden Beispiele für Singletons einer langen Beschreibung vorziehen, deshalb habe ich ein paar vorbereitet.
Hilfsklasse
Beginnen wir mit einem einfachen Beispiel: der Utility-Klasse. Die Utility-Klasse beherbergt viele Utility-Funktionen, die überall in Ihrer Anwendung verwendet werden. Wenn Sie diese Funktionen in einer einzigen Klasse zusammenfassen, wird Ihr Code übersichtlicher, da Sie alle Ihre Utility-Funktionen leicht finden können und sie Ihre Methoden nicht überladen.
//Class UtilityClass
singleton Class constructor()
Function utilityFunction1()
Function utilityFunction2()
...
Von diesem Moment an können Sie Ihre Dienstprogramme mit dieser Zeile aufrufen:
cs.UtilityClass.me.utilityFunction1()
Websocket-Server-Wrapper
Eine weitere klassische Verwendung von Singletons ist die Erstellung von Wrappern für Objekte, die überall in Ihrer Anwendung zugänglich sein sollen, wie z. B. ein Websocket-Server. Betrachten wir diese Klasse:
//WebSocketServerWrapper
singleton Class constructor()
$handler:=cs.myWebsocketServerHandler.new()
This.webSocketServer:=4D.WebSocketServer.new($handler; {dataType: “object”})
Function terminate()
This.webSocketServer.terminate()
Von diesem Moment an können Sie Ihren Webserver durch einen Aufruf starten:
CALL WORKER(“WebSocketServerWorker”; Formula(cs.WebSocketServerWrapper.me))
Und beenden Sie ihn durch den Aufruf von:
CALL WORKER(“WebSocketServerWorker”; Formula(cs.WebSocketServerWrapper.me.terminate()))
Factory
Im dritten Beispiel wird ein Entwurfsmuster implementiert: die Factory. Die Factory ist für die Erstellung neuer Instanzen von Objekten zuständig, in diesem Fall von Fahrzeugobjekten, die von einer VehicleFactory stammen:
//Class VehicleFactory
property vehicleBuilt:=0 //Number of vehicles built by the factory
shared singleton Class constructor()
shared Function buildVehicle($type : Text)->$vehicle : cs.Vehicle
Case of
: $type="car"
$vehicle:=cs.Car.new()
: $type="truck"
$vehicle:=cs.Truck.new()
: $type="sport car"
$vehicle:=cs.SportCar.new()
: $type="motorbike"
$vehicle:=cs.Motorbike.new()
Else
$vehicle:=cs.Car.new()
End case
This.vehicleBuilt+=1
Da es sich um ein Singleton handelt, können Sie diese VehicleFactory aufrufen, um ein neues Fahrzeug von überall in Ihrem Prozess (oder Ihrer Anwendung, wenn Sie sie gemeinsam nutzen) mit einer einzigen Zeile zu erhalten:
$vehicle:=cs.VehicleFactory.me.buildVehicle("truck")
In diesem Fall tut diese Fabrik nicht viel, außer die Anzahl der gebauten Fahrzeuge zu zählen, bevor sie Ihr neues Fahrzeug zurückgibt, aber ich denke, Sie verstehen die Idee.
Wenn Sie möchten, dass die VehicleFactory in Ihrer gesamten Anwendung eindeutig ist, müssen Sie das Schlüsselwort shared hinzufügen. Die Funktion buildVehicle verändert die VehicleFactory (indem sie This.vehicleBuilt inkrementiert), daher müssen Sie ihr ebenfalls das Schlüsselwort shared hinzufügen, damit use und end use automatisch beim Aufruf und Beenden der Funktion aufgerufen werden.
Werte zwischen Prozessen
Eine weitere klassische Verwendung von Singletons ist das Hosten von prozessübergreifenden Werten, die Sie in Ihrer gesamten Anwendung verwenden werden. Hier ist ein Beispiel für ein solches Singleton:
//Class InterprocessSingleton
shared singleton Class constructor()
This.version:=2
This.buildNumber:=1056
Von dort aus können Sie z. B. sehr einfach auf Ihre globalen Variablen zugreifen:
cs.InterprocessSingleton.me.version
Tipp: Manchmal möchten Sie Ihre Interprozess-Werte beim Start Ihrer Anwendung initialisieren, typischerweise in der Methode On Application Startup, und in einem solchen Fall müssen Sie dem Singleton-Konstruktor Parameter übergeben, wie im folgenden Beispiel:
//Class InterprocessSingleton
shared singleton Class constructor($file : 4D.File)
$json:=$file.getText()
$settingsJson:=JSON Parse($json)
This.version:=$settingsJson.version
This.buildNumber:=$settingsJson.buildNumber
In diesem Fall stammen die Werte von version und buildNumber aus einer JSON-Datei. Im Falle von Singletons gibt es eine Möglichkeit, den Konstruktor mit Parametern aufzurufen, indem man die new()-Funktion aufruft. Zum Beispiel in unserem Fall:
$myFile:=File("/RESOURCES/settings.json")
cs.InterprocessSingleton.new($myFile)
Sie müssen darauf achten, dass der Aufruf von new() das erste Mal ist, dass auf das Singleton zugegriffen wird. Ein Aufruf von new(), nachdem auf das Singleton zugegriffen wurde, ruft den Klassenkonstruktor nicht mehr auf.
Besonderes Dankeschön
Ein besonderer Dank geht an Chris Belanger, der vorgeschlagen hat, das me-Attribut für den Zugriff auf ein Singleton hinzuzufügen.
Ich hoffe, Singletons sind für Sie genauso spannend wie für uns. Wenn Sie Fragen dazu haben, können Sie diese gerne im 4D Forum stellen.