Singleton in der Applikation verwenden.

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Antworten
charlytango
Beiträge: 843
Registriert: Sa 12. Sep 2015, 12:10
OS, Lazarus, FPC: Laz stable (2.2.6, 3.x)
CPU-Target: Win 32/64, Linux64
Wohnort: Wien

Singleton in der Applikation verwenden.

Beitrag von charlytango »

Hi,

in http://www.lazarusforum.de/viewtopic.php?f=18&t=11936 bekam ich den Rat für Programmeinstellungen oder Datenbankanbindungen das Singleton-Konzept zu verwenden. Das klappt mittlerweile ganz gut mit der Erkenntnis, dass es DEN code für ein Singleton-Pattern nicht gibt. Der scheint je nach Programmierer und Verwendungszweck/Anforderungen sehr variabel zu sein.

Nachdem die Programmaufgaben die ich in Singletons packe für das gesamte Programm zur Verfügung stehen müssen und auch beim Start im wesentlichen auch vorhanden sein sollten erzeuge ich die Objekte im initialization Abschnitt der Unit.

Klappt alles soweit gut.

Nur der Zugriff zu den Objekten direkt im restlichen Code ist in einigen Singleton-Varianten recht aufwendig.

Code: Alles auswählen

 
procedure xy
var
  s: TMySettings; // class
begin
  s := GetMySettings; // GetMySettings nehme ich jetzt mal als als Funktion die den Singleton zurückgibt
  s.Irgendwas := Wert;
end;


das von braunbär vorgeschlagene Konstrukt scheint mir etwas zu aufwendig, um nur eine Einstellung auszulesen.
Daher habe ich die Variable des Singleton im gesamten Programm sichtbar gemacht.

Code: Alles auswählen

interface
....
var
 MySettings: TMySettings = nil;
 
implementation
....


was es im Code erlaubt einfach

Code: Alles auswählen

MySettings.Irgendwas := Wert;

zu schreiben

Gibt es dazu Einwände ?

Dann ist auch noch der zeitliche Ablauf der Erstellung der verschiedenen Singleton-Objekte und die Rückmeldungen an den Benutzer ein Thema.
Wie schafft man es z.B. das Objekt mit den Einstellungen vor dem Objekt mit den Datenbankzugriffen zu erstellen ? Nur die Reihenfolge in der uses-Klausel? Oder das Objekt so zu strukturieren dass erst nur das Objekt ohne Funktion erzeugt wird (wobei dann die Reihenfolge egal wäre) und mit einer init-Prozedur an einer bestimmten Stelle das Objekt erst mit Leben füllt.

Und wie bekomme ich eine Rückmeldung z.B. in einer Splash Screen hin? z.B. irgend ein Logo auf einer Form und z.B. in einem Memo was gerade passiert um das Programm zu starten. Mir geht es nicht darum wie man eine Splash-Screen erstellt und einbindet sondern wie die diversen Singleton-Objekte damit kommunizieren können?

THX

Michl
Beiträge: 2505
Registriert: Di 19. Jun 2012, 12:54

Re: Singleton in der Applikation verwenden.

Beitrag von Michl »

Nur so am Rande. Ich finde es nicht so günstig, großartig irgendwelche Operationen vor Programmstart zu machen (im Initialization-Block, wegen Logging, Exceptionbehandlung etc.). Wollte ich auch schon in dem anderen Thread schreiben, da aber jeder einen anderen Geschmack hat, und letztlich jeder machen kann, was er will, hatte ich es gelassen.

Eine Möglichkeit, die ich oft nutze, ist On-Demand, also die Erstellung einer Unique-Instanz auf Aufforderung. Der Vorteil ist, daß sie erst erstellt wird, wenn tatsächlich auf sie zugegriffen wird. Ein Nachteil ist, daß diese sich nicht so gut debuggen lässt (Debugger springt als erstes in die Getter-Funktion).

Ansonsten ist das Vorgehen gleich, wie du es bisher hast und auch gut so.

Würde dann in etwa so ausehen:

Code: Alles auswählen

unit Unit1;
 
{$mode objfpc}{$H+}
 
interface
 
type
 
  { TFoo }
 
  TFoo = class
  public
    procedure WriteTest;
  end;
 
function Foo: TFoo;
 
implementation
 
var
  FFoo: TFoo;
 
function Foo: TFoo;
begin
  if not Assigned(FFoo) then
    FFoo := TFoo.Create;
  Result := FFoo;
end;
 
{ TFoo }
 
procedure TFoo.WriteTest;
begin
  writeln('TFoo.WriteTest');
end;
 
initialization
  FFoo := nil;
 
finalization
  if Assigned(FFoo) then
    FFoo.Free;
 
end.


Code: Alles auswählen

program project1;
 
uses Unit1;
 
begin
  Foo.WriteTest;
end.

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection; 

Benutzeravatar
kupferstecher
Beiträge: 418
Registriert: Do 17. Nov 2016, 11:52

Re: Singleton in der Applikation verwenden.

Beitrag von kupferstecher »

Das Problem an Initialization ist, dass es vor der LCL-Initialisierung stattfindet, also bevor auch nur eine GUI-Komponente initialisiert wurde. Dadurch ist es oft nicht nutzbar. Ich mach immer eine Unit mit allen Initialisierungen in einer Procedur GlobalInit. Diese ruf ich in der LPR-Datei vor Application.Run auf.

Benutzeravatar
m.fuchs
Lazarusforum e. V.
Beiträge: 2636
Registriert: Fr 22. Sep 2006, 19:32
OS, Lazarus, FPC: Winux (Lazarus 2.0.10, FPC 3.2.0)
CPU-Target: x86, x64, arm
Wohnort: Berlin
Kontaktdaten:

Re: Singleton in der Applikation verwenden.

Beitrag von m.fuchs »

charlytango hat geschrieben:

Code: Alles auswählen

 
procedure xy;
var
  s: TMySettings; // class
begin
  s := GetMySettings; // GetMySettings nehme ich jetzt mal als als Funktion die den Singleton zurückgibt
  s.Irgendwas := Wert;
end;
das von braunbär vorgeschlagene Konstrukt scheint mir etwas zu aufwendig, um nur eine Einstellung auszulesen.

Code: Alles auswählen

 
procedure xy;
begin
  GetMySettings.Irgendwas := Wert;
end;
Wie wäre es so? Ist dann nur noch eine Zeile und keine gesonderte Deklaration mehr.
Software, Bibliotheken, Vorträge und mehr: https://www.ypa-software.de

Benutzeravatar
fliegermichl
Lazarusforum e. V.
Beiträge: 1432
Registriert: Do 9. Jun 2011, 09:42
OS, Lazarus, FPC: Lazarus Fixes FPC Stable
CPU-Target: 32/64Bit
Wohnort: Echzell

Re: Singleton in der Applikation verwenden.

Beitrag von fliegermichl »

Man kann ja mittlerweile auch Properties ausserhalb von Klassen definieren.

Code: Alles auswählen

 
interface
 function GetMySettings : TMySettings;
 property MySettings : TMySettings read GetMySettings;
implementation
var fMySettings : TMySettings;
 
function GetMySettings : TMySettings;
begin
 if not Assigned(fMySettings) then fMySettings := TMySettings.Create;
 Result := fMySettings;
end;
 


Dann kann man einfach MySettings.Irgendwas := Irgendwer verwenden.

charlytango
Beiträge: 843
Registriert: Sa 12. Sep 2015, 12:10
OS, Lazarus, FPC: Laz stable (2.2.6, 3.x)
CPU-Target: Win 32/64, Linux64
Wohnort: Wien

Re: Singleton in der Applikation verwenden.

Beitrag von charlytango »

kupferstecher hat geschrieben:Das Problem an Initialization ist, dass es vor der LCL-Initialisierung stattfindet, also bevor auch nur eine GUI-Komponente initialisiert wurde. Dadurch ist es oft nicht nutzbar. Ich mach immer eine Unit mit allen Initialisierungen in einer Procedur GlobalInit. Diese ruf ich in der LPR-Datei vor Application.Run auf.


Also gibt es wohl wie immer keinen Königsweg :(
Daher wird es eine Kombination aus den Strategien aller beteiligten Poster -- Danke für eure Mühe!

@Kupferstecher:
Rufst du in der .LPR Datei deine Init-Prozedur folgendermaßen auf ?

Code: Alles auswählen

Unit4711.GlobalInit;


oder gibt es da andere Vorschriften in der .LPR ?

Eine Frage wurde noch nicht beantwortet:
Wie können die Singleton-Objekte (nehmen wir mal an es handelt sich um ein Objekt zur Datenbankverbindung) mit der Splashscreen kommunizieren?

Auszug aus möglichen Meldungen f die Splashscreen:
    Initialisierungsdaten f DB laden
    DB Verbindung aufbauen
    Programmeinstellungen aus der DB laden
    DLL xyz laden
    Rechtesystem erzeugen
    Logging vorbereiten
    Menüs erzeugen
und andere mehr. Für mittlere und größere Projekte ist einiges zu tun bevor die Applikation einsatzbereit ist. Den User da ohne Info warten zu lassen wäre schlechter Stil.

Wie löst ihr so eine Aufgabe? (An Forumsadmin -- gern auch in einen eigenen Thread verschieben)

Benutzeravatar
kupferstecher
Beiträge: 418
Registriert: Do 17. Nov 2016, 11:52

Re: Singleton in der Applikation verwenden.

Beitrag von kupferstecher »

charlytango hat geschrieben:Rufst du in der .LPR Datei deine Init-Prozedur folgendermaßen auf ?

Ja, genau. So sieht das dann bei mir aus, händisch habe ich nur "GlobalInit" und "GlobalDeinit" eingetragen:

Code: Alles auswählen

begin
  RequireDerivedFormResource := True;
  Application.Initialize;
  Application.CreateForm(TMainForm, MainForm);
  GlobalInit;    // <-- Initialisierung
  Application.Run;
  GlobalDeinit;  // <-- Aufräumen
end.

Antworten