Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Lose Funktionen oder als Funktion in Klasse (https://www.delphipraxis.net/213366-lose-funktionen-oder-als-funktion-klasse.html)

norwegen60 19. Jul 2023 08:48

Lose Funktionen oder als Funktion in Klasse
 
Hallo

ich hätte gerne mal ein aktuelles Feedback, ob es gewichtige Gründe gibt, die dafür/dagegen sprechen, eine Liste von Funktionen besser als lose Funktionen oder besser als Funktionen in einer Klasse zu definieren.

Also so
Delphi-Quellcode:
interface

function HandleWorkflow1: Boolean;
function HandleWorkflow2: Boolean;
function HandleWorkflow3: Boolean;
function HandleWorkflow4: Boolean;

var
  Variable1..3:Integer;

implementation

....
oder so
Delphi-Quellcode:
interface

type
  THandleWorkflow = Class
  private
    FVariable1 : Integer;
    FVariable2 : Integer;
    FVariable3 : Integer;

    procedure GetVariable2: Integer;
    procedure GetVariable3: Integer;
    procedure SetVariable3(const Value: Integer);
  public
    property Variable1 : Integer read FVariable1 write FVariable1;
    property Variable2 : Integer read GetVariable2 write FVariable2;
    property Variable2 : Integer read GetVariable3 write SetVariable3;

    constructor Create;
    destructor Destroy; override;

    function HandleWorkflow1: Boolean;
    function HandleWorkflow2: Boolean;
    function HandleWorkflow3: Boolean;
    function HandleWorkflow4: Boolean;
  end;

implementation

....
Ich habe dazu den Beitrag https://www.delphipraxis.net/46036-lose-funktionen-klassen-kapseln.html gefunden, aber der ist von 2005 und da war OOP noch nicht ganz so genutzt wie heute

Ich sehe folgende Nach- und Vorteile der Klasse

Nachteile der Klasse
- Aufbau ist auf den ersten Blick aufwendiger (z.B. Variablendefinition bestehen aus bis zu 4 Zeilen (FVariable, SetVariable, GetVariable, property Variable)
- Klasse muss Created und Destroyed werden
- Funktionsaufruf etwas länger, da immer auch Classname vorangestellt werden muss (HandlingWorkflow.HandleWorkflow1)


Vorteile der Klasse
+ Über [Ctrl + C] wird Funktionsblock und FVariablen automatisch angelegt (Dadurch ist etwas aufwändigere Aufbau auch kein wirklicher Nachteil)
+ Bei Verwendung von Get/Set kann schön debuggt (oder geloggt) werden, wenn sich eine Variable ändert
+ Eher formalistisch und doch schön: Durch [Ctrl + C] sind Funktionen im Code alphabetisch sortiert angelegt
+ Nach Eingabe des Classname werden automatisch alle verfügbaren Elemente angezeigt

Gibt es andere Vor-/Nachteile?
Gibt es beim späteren Handling in der EXE Vor-/Nachteile? (Resourcenverbrauch, Speed, ...)

Grüße
Gerd

Der schöne Günther 19. Jul 2023 09:09

AW: Lose Funktionen oder als Funktion in Klasse
 
Zitat:

Zitat von norwegen60 (Beitrag 1524692)
Variablendefinition bestehen aus bis zu 4 Zeilen (FVariable, SetVariable, GetVariable, property Variable)

Nein. Warum muss das so sein? Niemand braucht diese Properties. Eine normale Variable tut es auch. Null Mehraufwand.

Zitat:

Zitat von norwegen60 (Beitrag 1524692)
Klasse muss Created und Destroyed werden

Nein. Ist die Methode abhängig von einer individuellen Instanz deiner Klasse? Wenn nicht, dann ist es eine Klassenmethode ("statisch").

Eine Klasse macht das deutlich besser testbar ("Unit-Tests"). Der Code ist austauschbar. Du kannst mehrere unabhängige und sauber getrennte Instanzen (z.B. über mehrere Threads) haben, anstatt das alles mit den selben globalen Variablen jongliert. Objektorientierte Programmierung macht viele moderne Entwicklungsmuster ("Patterns") erst möglich.

Ich würde wirklich noch mal ein Tutorial über OOP lesen, insbesondere die drei Stichworte Datenkapselung, Polymorphie und Vererbung. Zur Datenkapselung gehört insbesondere der von dir erkannte Vorteil, dass die Code Completion (und damit auch der Entwickler) auf den ersten Blick sieht und weiß, was hier thematisch zusammengehört, anders als bei einem Sack von global herumschlabbernden Prozeduren.

himitsu 19. Jul 2023 09:59

AW: Lose Funktionen oder als Funktion in Klasse
 
Jupp, vor allem die Code-Completion ist ein enormer Vorteil.
OK, statt der Variable, bzw. des Klassnamen könnte man auch den Unitnamen dafür verwenden. :stupid:

Ob Variable oder Property scheint erstmal egal zu sein.
Aber Ob
Delphi-Quellcode:
  public
    Variable1 : Integer;
oder
Delphi-Quellcode:
  private
    FVariable1 : Integer;
  public
    property Variable1 : Integer read FVariable1 write FVariable1; // bzw. mit Getter und/oder Setter
Klar, kann man später immernoch aus der Variable ein Property machen, aber wenn man diese Variable z.B. schon irgendwo als Var-Parameter übergeben hat, dann knallt es erstmal.
Ist es schon ein Property, dann lassen sich nahezu immer problemlos auch einfach Getter/Setter nachrüsten.




Eventuell dann noch ein Singleton dafür erstellen.
Wenn als normale Class, dann hat man eine globale Instanz für, so als wären die Funktionen einzeln "direkt" aufrufbar.
Und zusätzlich (falls man sich nichts versperrt (globale Variablen uns so), dann lassen sich z.B. für Threads und Co. auch unabhängige weitere Instanzen erstellen/nutzen.
Delphi-Quellcode:
var
  HandleWorkflow: THandleWorkflow; // globale Variable z.B. im Initialization oder in einem Class-Constructor erstellt
// oder als "function HandleWorkflow: THandleWorkflow;", welches den Singleton liefert


HandleWorkflow.Variable1 := 123;
So wie diese perversen Form-Vaiablen, welche Delphi standardmäßig bereitstellt.



Wenn es "nur" als einzelne Kapselung dienenn soll, dann Class-Function und Class-Property, sowie Class-Var
Delphi-Quellcode:
type
  THandleWorkflow = class
  private class var
    FVariable1 : Integer;
  private
    class procedure SetVariable1(Value: Integer);
  public
    class constructor Create; // wird automatisch aufgerufen, wenn die Unit initialisiert wird
    class destructor Destroy; // wird automatisch aufgerufen, wenn die Unit finalisiert wird
 
    class property Variable1 : Integer read FVariable1 write SetVariable1; // bzw. mit Getter und/oder Setter
end;

THandleWorkflow.Variable1 := 123;
[/DELPHI]

Wobei ich hier inzwischen auch gern mal Records dafür benutze.
Delphi-Quellcode:
type
  THandleWorkflow = record
    ...
    class procedure SetVariable1(Value: Integer); static;
    ...
  end;

Uwe Raabe 19. Jul 2023 10:08

AW: Lose Funktionen oder als Funktion in Klasse
 
Um die globalen Variablen und Funktionen in einer Klasse zu kapseln, ohne davon eine Instanz erzeugen zu müssen, könnte man das so lösen:
Delphi-Quellcode:
type
  TWorkflow = class
  public
  class var
    Variable1: Integer;
    Variable2: Integer;
    Variable3: Integer;
    class function HandleWorkflow1: Boolean;
    class function HandleWorkflow2: Boolean;
    class function HandleWorkflow3: Boolean;
    class function HandleWorkflow4: Boolean;
  end;
Beim Zugriff muss dann immer ein
Delphi-Quellcode:
TWorkFlow.
davor gestellt werden.

Alternativ geht auch ein record:
Delphi-Quellcode:
type
  TWorkflow = record
  class var
    Variable1: Integer;
    Variable2: Integer;
    Variable3: Integer;
    class function HandleWorkflow1: Boolean; static;
    class function HandleWorkflow2: Boolean; static;
    class function HandleWorkflow3: Boolean; static;
    class function HandleWorkflow4: Boolean; static;
  end;
Wenn man dann noch die Redundanz in den Methodennamen (in der Praxis wohl dann auch bei den Variablen) eliminiert, kommt sowas raus:
Delphi-Quellcode:
type
  TWorkflow = record
  class var
    Variable1: Integer;
    Variable2: Integer;
    Variable3: Integer;
    class function Handle1: Boolean; static;
    class function Handle2: Boolean; static;
    class function Handle3: Boolean; static;
    class function Handle4: Boolean; static;
  end;

norwegen60 19. Jul 2023 10:25

AW: Lose Funktionen oder als Funktion in Klasse
 
Hallo zusammen,

da ich in einem Programm/DLL nur eine Instanz der Klasse benötige, habe ich so was perverses wir Delphi gemacht und das noch getoppt


ich habe so was
Delphi-Quellcode:
interface

type
  THandleWorkflow = Class
  private
    FVariable1 : Integer;
    FVariable2 : Integer;
    FVariable3 : Integer;

    procedure GetVariable2: Integer;
    procedure GetVariable3: Integer;
    procedure SetVariable3(const Value: Integer);
  public
    property Variable1 : Integer read FVariable1 write FVariable1;
    property Variable2 : Integer read GetVariable2 write FVariable2;
    property Variable2 : Integer read GetVariable3 write SetVariable3;

    constructor Create;
    destructor Destroy; override;

    function HandleWorkflow1: Boolean;
    function HandleWorkflow2: Boolean;
    function HandleWorkflow3: Boolean;
    function HandleWorkflow4: Boolean;
  end;

var
  HandleWorkflow :THandleWorkflow ;

implementation




initialization

HandleWorkflow := THandleWorkflow .Create;

finalization

HandleWorkflow .Free;

end.

Ich habe nie ganz verstanden, warum das so schlimm ist wenn man sicher nur eine Instanz braucht

Dass ich Variablen nicht als public anlege sondern als property ist zur Gewohnheit geworden

Das
Delphi-Quellcode:
class function HandleWorkflow1: Boolean;
erschließt sich mir nicht ganz. Ich hätte gesagt, dass es letztlich das gleiche ist wie ich es mit initialize und finalization mache

Ich habe jahrelang so eine Klasse wie oben - aber nur mit procedure udn function - ohne Create und Free benutzt. Ohne Probleme.

Grüße
Gerd

Uwe Raabe 19. Jul 2023 10:30

AW: Lose Funktionen oder als Funktion in Klasse
 
Zitat:

Zitat von norwegen60 (Beitrag 1524699)
Ich habe jahrelang so eine Klasse wie oben - aber nur mit procedure udn function - ohne Create und Free benutzt. Ohne Probleme.

Natürlich, und das ist ja auch nicht falsch. Wir zeigen hier lediglich andere Wege auf.

Edelfix 19. Jul 2023 10:53

AW: Lose Funktionen oder als Funktion in Klasse
 
Zu "Über [Ctrl + C] wird Funktionsblock und FVariablen automatisch angelegt" habe ich eine Frage.

Es wird jedes mal eine Set Procedure angelegt. Lässt sich das ausschalten?

DeddyH 19. Jul 2023 11:15

AW: Lose Funktionen oder als Funktion in Klasse
 
Nicht, dass ich wüsste. Aber wenn man etwas mehr tippt:
Delphi-Quellcode:
...
property Blubb: integer read FBlubb write FBlubb;
dann wird auch kein Setter angelegt.

Uwe Raabe 19. Jul 2023 11:28

AW: Lose Funktionen oder als Funktion in Klasse
 
Oder man verwendet das propf Template (oder MMX).

jaenicke 19. Jul 2023 11:39

AW: Lose Funktionen oder als Funktion in Klasse
 
Ein Vorteil der Kapselung in Klassenmethoden ist, dass man sofort sieht, wo eine Methode herkommt. Wenn man also Code kopiert oder den Code restrukturiert, ist es so deutlich einfacher. Hat man nur eine einzelne Methode, die der Compiler nicht findet, ist diese schlechter zu finden.

Natürlich könnte man auch immer den Unitnamen davonschreiben, aber das wäre dann optional. Den Klassennamen kann man hingegen nicht weglassen, womit sichergestellt ist, dass das auch jeder im Team macht.

norwegen60 19. Jul 2023 12:00

AW: Lose Funktionen oder als Funktion in Klasse
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1524700)
Zitat:

Zitat von norwegen60 (Beitrag 1524699)
Ich habe jahrelang so eine Klasse wie oben - aber nur mit procedure udn function - ohne Create und Free benutzt. Ohne Probleme.

Natürlich, und das ist ja auch nicht falsch. Wir zeigen hier lediglich andere Wege auf.

War auch nicht als Kritik gemeint
Es ging mir eher drum, zu erfahren, warum manche die globale Deklaration so verdammen. Ich weiß aber auch, dass man damit sehr unsauberen Code erzeugen kann.
Bei der Verwendung ohne Create hätte ich gedacht, dass es falsch ist und ich mich irgendwann mit einer neueren Delphi-Version gewundert hätte, warum der Code nicht mehr läuft

himitsu 19. Jul 2023 12:15

AW: Lose Funktionen oder als Funktion in Klasse
 
"ohne Create" ... erstmal muß man dann eine Variable benutzen,
während man bei Class-Proicedure direkt den Typ nehmen kann.

Und so lange du auf nichts von der Klassen-Instanz zugreifst (z.B. Felder/Variablen), also das Self nie benutzt wird, dann gibt es auch keine Fehler, egal ob die Variable NIL oder sonstwie uninitialisiert (zufällig) ist.



Vorteil von Record statt Class ... man muß sich keine Gedanken um das Create machen, und vor allem auch nicht um das Free/Destroy. :angle:



Vorteil von Class-Constructor gegenüber Initialization, also für gesharetem Code (wäre schön, wenn es Emba mal nutzen würde),
dass bei Nichtbenutzung der Klasse sie auch garnicht erst einkompiliert gelinkt wird.

Steht der Create-Code in der Initialization, dann wird dadurch immer die Klasse "benutzt", auch wenn sie niemand nutzt.

Uwe Raabe 19. Jul 2023 13:23

AW: Lose Funktionen oder als Funktion in Klasse
 
Am Ende sind solche class var bzw. class function (insbesondere wenn auch noch static) auch nichts anderes als globale Variablen bzw. Methoden. Es ändert sich lediglich die Syntax des Zugriffs.

Die Namensgebung wird auch etwas einfacher (obwohl die ja nie einfach ist), da man mit dem Klassennamen als Präfix eine gewisse Kapselung erhält, die bei echten globalen Variablen und Methoden ja nur über die Namensgebung erreicht werden kann. Wer hat nicht schon darüber geflucht, dass mit dem Einfügen einer Unit in die uses-Anweisung plötzlich eine gleichnamige Variable oder Methode im Scope weiter vorne steht und bestenfalls vom Compiler bemängelt wird. Die Lösung des Voranstellen des Unitnamens wird mit dem class/record Ansatz in der Regel überflüssig.

Nebenbei: Ist das nicht lästig, wenn man solche mit Unitnamen qualifizierte Zugriffe im Code hat und die Unit dann plötzlich nicht mehr Sysutils oder Windows, sondern System.Sysutils oder Winapi.Windows heißt. Da helfen die Unit-Scope-Names nämlich auch nicht.

Blup 26. Jul 2023 17:10

AW: Lose Funktionen oder als Funktion in Klasse
 
Mir scheint wichtiger wie man auf solche globalen Resourcen zugreift.

Ich schotte den Anwendungsfall durch ein Provider von der Anwendungsumgebung ab.
Das kann eine abstrakte Klasse sein oder ein Interface, das den Zugriff kapselt.
Darüber werden alle vom Anwendungsfall benötigten globalen Funktionen, Methoden, Interfaces und Variablen bereitgestellt.

In der Anwendung werden die Zugriffe einfach nur durchgereicht.
Im Testfall kann eine ganz konkrete Umgebung simuliert werden (Mock).

Der Überblick über die Abhängigkeiten erleichtert auch die Aufwandschätzung und Realisierung von Erweiterungen oder
Migration eines Anwendungsfalls (z.B. Kommandozeile, Dienstprogramm, Webservice usw.).

freimatz 27. Jul 2023 06:53

AW: Lose Funktionen oder als Funktion in Klasse
 
Zitat:

Zitat von norwegen60 (Beitrag 1524710)
Es ging mir eher drum, zu erfahren, warum manche die globale Deklaration so verdammen. Ich weiß aber auch, dass man damit sehr unsauberen Code erzeugen kann.

Für Variablemen meine ich: Nicht kann, es ist schlechter Code. ;-)
Zum Warum gibt es im Netz vieles zu finden z.B.:
https://www.c-plusplus.net/forum/top...en-schlecht/39
In der Regel reicht eine einzigste eigene globale Variable für eine grössere Delphi-Anwendung.

QuickAndDirty 27. Jul 2023 10:06

AW: Lose Funktionen oder als Funktion in Klasse
 
Ich habe hier ein Programm , das seit 1995 gewachsen ist.
Es ist voller globaler Variablen und nutzt intensiv komponenten (Datenbank Komponenten, Timer)die auf Formularen liegen...
Es gibt eine aus sicht des Users GUI freie version von dem Programm das als Webservice läuft.
Leider kann es immer nur eine anforderung nach der anderen bearbeiten...denn man kann ja Units mit ihren globalen variablen nicht für jeden Threadcontext neu instanzieren.
Hätten wir alles in Singletons abgespeichert hätte man die leicht zentral erweitern können dass es jeweils eine Instanz pro thread gibt.
In sofern sind Globale Variablen vielleicht langfristig gesehen nicht sehr flexible.

himitsu 27. Jul 2023 13:18

AW: Lose Funktionen oder als Funktion in Klasse
 
Es gibt dann noch die Lösung von SubProzessen,
also wenn man es innerhalb des Programms nicht trennen kann, dann wird im Hintergrund z.B. je Connection eine neue Instanz gestartet und ihr die Aufgabe zugeteilt.

Elvis 28. Jul 2023 07:29

AW: Lose Funktionen oder als Funktion in Klasse
 
Zitat:

Zitat von QuickAndDirty (Beitrag 1524958)
Hätten wir alles in Singletons abgespeichert hätte man die leicht zentral erweitern können dass es jeweils eine Instanz pro thread gibt.

Singletons sind nur hauchdünn marginal besser als globale variablen.
Vor allem wenn sie die shared instance der anderen Singletons nutzen.

dummzeuch 28. Jul 2023 09:17

AW: Lose Funktionen oder als Funktion in Klasse
 
Zitat:

Zitat von Elvis (Beitrag 1524996)
Zitat:

Zitat von QuickAndDirty (Beitrag 1524958)
Hätten wir alles in Singletons abgespeichert hätte man die leicht zentral erweitern können dass es jeweils eine Instanz pro thread gibt.

Singletons sind nur hauchdünn marginal besser als globale variablen.
Vor allem wenn sie die shared instance der anderen Singletons nutzen.

Exakt: Singleton ist eigentlich eher ein Antipattern.

Elvis 28. Jul 2023 13:11

AW: Lose Funktionen oder als Funktion in Klasse
 
Ist sicherlich absolut ok für services, die stateless arbeiten und/oder immutable options objekte.

Also Dinge, die sich nicht zur Laufzeit ändern können, können als Singleton oder Default instance angeboten werden.
Vor allem wenn man bedenken über sinnlos häufiges malloc hat. (in Delphi wohl weniger problematisch dank deterministischem manuellen Speichermanagement)

Aber: es ist viel sinnvoller sowas als Parameter an eine Methode oder einen ctor zu geben (readonly field/property).
Dann kann man am Ende entscheiden was man wie reinwirft. Denn Singletons lassen sich fast unmöglich für Tests mocken.

Rollo62 28. Jul 2023 13:18

AW: Lose Funktionen oder als Funktion in Klasse
 
Zitat:

Zitat von dummzeuch (Beitrag 1525001)
Exakt: Singleton ist eigentlich eher ein Antipattern.

Mag sein, trotzdem nehme ich Singletons gerne für Objekte von denen es aus logischen, physischen oder Hardwaregründen nur eines geben kann, z.B.
ein TApp Objekt (Basic App Daten), ein TOrientation Objekt (Portrait/Landscape), ein TDisplays Objekt (Daten zu Monitoren), ...

Die baue ich dann möglichst so, dass ich problemlos überall nutzen kann, um die drunter-liegenden, realen Dinge zu kapseln.
Das ist auch oft nur ein einmaliges Ermittlen von Basis-Daten, was dann oft in der App nur abgefragt wird.
Da macht ein Singleton für mich schon sehr viel Sinn, um die ansonsten ungeschützen Zugriffe besser zu kapseln und zu entkoppeln.

jaenicke 28. Jul 2023 16:05

AW: Lose Funktionen oder als Funktion in Klasse
 
Zitat:

Zitat von Elvis (Beitrag 1525011)
Denn Singletons lassen sich fast unmöglich für Tests mocken.

Kommt drauf an wie man sie umsetzt. ;-)

Ich arbeite dazu hiermit:
https://www.delphipraxis.net/213199-...ng-dlls-c.html

Das hat den Vorteil, dass man das Singleton global registriert, es aber per Interface entkoppelt ist. Deshalb kann man es wie ein Singleton nutzen, aber problemlos mocken.

Uwe Raabe 28. Jul 2023 16:44

AW: Lose Funktionen oder als Funktion in Klasse
 
Ein Singleton ist ja erstmal nur eine zentrale Instanz, die nur einmal im Programm existiert. Das bedeutet aber nicht, dass diese Instanz immer die gleiche Implementierung haben muss.

Zitat:

Zitat von https://de.wikipedia.org/wiki/Singleton_(Entwurfsmuster)
Das Singleton findet Verwendung, wenn
  • nur ein Objekt zu einer Klasse existieren darf und ein einfacher Zugriff auf dieses Objekt benötigt wird oder
  • das einzige Objekt durch Unterklassenbildung spezialisiert werden soll.

Für Unittests kann man ja auch einen Mock als Singleton verwenden. Auch ein Null-Object als Default-Implementation ist zulässig, wenn keine andere Implementierung benötigt wird.

Wenn das nicht ginge, könnte man die Singleton-Klassen ja gleich als
Delphi-Quellcode:
sealed
deklarieren.

Elvis 30. Jul 2023 16:52

AW: Lose Funktionen oder als Funktion in Klasse
 
ein klassisches singleton sorgt dafür dass es nur eine Instanz geben kann.
entweder weil die Klasse einen private ctor hat, oder du verschiedene static readonly References hast.

Du kannst sowas nur sinnvoll mocken, wenn du sie nicht als singletons verwendest, sondern als ctor oder Methoden-Parameter.

Aber wenn du sie so verwendest hast du auch fast keine Nachteile des Singletons mehr.

Sorry für mein c#, keine mich mit modernem Delphi nicht gut genug aus…
Das ist, als ob du einen IEqualityComparer<string> als Parameter nimmst, und den in deinem IOC container statisch als „singleton“ registrierst.

Allerdings kannst du jederzeit das interface mocken um edge cases in einem test zu entdecken.
Dein code geht ja nicht zu der einen statischen Stelle, um sich StringComparer.Ordinal zu holen.
Das gibst du ihm ja nur indirekt per DI oder Parameter.

freimatz 31. Jul 2023 08:04

AW: Lose Funktionen oder als Funktion in Klasse
 
Zitat:

Zitat von Rollo62 (Beitrag 1525012)
Zitat:

Zitat von dummzeuch (Beitrag 1525001)
Exakt: Singleton ist eigentlich eher ein Antipattern.

Mag sein, trotzdem nehme ich Singletons gerne für Objekte von denen es aus logischen, physischen oder Hardwaregründen nur eines geben kann, z.B.
ein TApp Objekt (Basic App Daten), ein TOrientation Objekt (Portrait/Landscape), ein TDisplays Objekt (Daten zu Monitoren), ...

Und das halte ich für einen Denkfehler. Also die Meinung dass es nur eines geben kann. Man hat sich da schon oft geirrt.
- Die Software ist für eine Maschine. Schon, aber nun hat die Maschine plötzlich meherer Einheite die man steuern muss.
- Orientation? Nun will man die erste Seite quer die anderen längs
- Daten zu Monitoren - nun hat man sechs, will aber immer drei als ein Set behandeln. (Hat mich schon oft gestört dass Remote Desktop nur einer oder alle Montore kann)
Meistens ist es ja ok und allermeist bleibt es dann auch bei einem einzelnen Objekt. Flexibler ist man wenn man es nicht als Singleton implementiert.
Wir haben in der Firma auch Objekt, aber dort wird es konfiguriert ob der Dependency Container eine Klasse als Singleton erzeugt oder nicht.
Und damit kann man die eben auch Mocken.

Uwe Raabe 31. Jul 2023 09:35

AW: Lose Funktionen oder als Funktion in Klasse
 
Zitat:

Zitat von freimatz (Beitrag 1525068)
Und das halte ich für einen Denkfehler. Also die Meinung dass es nur eines geben kann. Man hat sich da schon oft geirrt.

Das mag vielleicht sein, aber die Entscheidung obliegt dem Entwickler der diese im Kontext seiner Anwendung macht. Eine pauschale Verurteilung von Singletons ist ebenso fehlgeleitet wie eine falsche Verwendung eines solchen. Natürlich gibt es Fälle, in denen ein Singleton die falsche Wahl ist, aber es gibt genügend Fälle wo das nicht gilt. Das kann man eben nicht pauschal beurteilen sondern sollte das immer vom konkreten Anwendungsfall oder Kontext abhängig machen. Und natürlich kann sich dieser Kontext im Lebenslauf einer Anwendung ändern, aber das ist doch des Entwicklers täglich Brot.

himitsu 31. Jul 2023 10:17

AW: Lose Funktionen oder als Funktion in Klasse
 
Wenn man das Singleton nicht hart in diese Klasse einbaut, sondern es auch ermöglicht dennoch weitere Instanzen davon zu erstellen (die globale "Variable" als Singleton, aber dennoch jetzt/zukünftig weitere Instanzen, z.B. für gewisse Threads),
dann hat man dennoch alle Möglichkeiten.

z.B. eine generische Implementation für die Variable, nicht im Objekt selber, oder zumindestens in einem gemeinsamen Vorfahren davon.
Dann lässt sich in Zukunft das z.B. so abändern, dass jeder Thread eine eigene Instanz bekommt (ThreadPool), welche vielleicht ihre Standardwerte von der globalen Hauptthread-Instanz oder einer anderen "Default"-Instanz erben/kopieren.

Uwe Raabe 31. Jul 2023 10:38

AW: Lose Funktionen oder als Funktion in Klasse
 
Ich verwende dafür gerne eine kleine generische Hilfsklasse, mit der ich (nahezu) jede Klasse bei Bedarf zu einem Singleton machen kann, ohne die Verwendung auf diesen Fall zu beschränken:
Delphi-Quellcode:
unit Common.Singleton;

interface

type
  TSingleton<T:class, constructor> = class
  strict private
  class var
    FInstance: T;
    class destructor DestroyClass;
  private
    class function GetInstance: T; static;
  public
    class property Instance: T read GetInstance;
  end;

implementation

class destructor TSingleton<T>.DestroyClass;
begin
  FInstance.Free;
end;

class function TSingleton<T>.GetInstance: T;
begin
  if FInstance = nil then
    FInstance := T.Create;
  result := FInstance;
end;

end.
Der Zugriff erfolgt dann über entsprechende Methoden wie z.B. diese:
Delphi-Quellcode:
function TranslationManager: TTranslationManager;
begin
  result := TSingleton<TTranslationManager>.Instance;
end;
oder je nach Geschmack auch als Klassenmethode:
Delphi-Quellcode:
class function TTranslationManager.Singleton: TTranslationManager;
begin
  Result := TSingleton<TTranslationManager>.Instance;
end;
Letzteres verdeutlicht die Singleton-Eigenschaft schon im Namen, aber das ist vielleicht nicht immer erwünscht.

jaenicke 31. Jul 2023 12:07

AW: Lose Funktionen oder als Funktion in Klasse
 
So eine Funktionalität mit automatischer Freigabe und einem Singleton als Klasse könnte ich in AppCentral natürlich auch einmal einbauen.

Bisher hatte ich das nur für Interfaces vorgesehen und würde es selbst auch nicht anders nutzen, aber wenn da Interesse besteht, wäre es kein Problem.

QuickAndDirty 1. Aug 2023 10:43

AW: Lose Funktionen oder als Funktion in Klasse
 
Zitat:

Zitat von dummzeuch (Beitrag 1525001)
Zitat:

Zitat von Elvis (Beitrag 1524996)
Zitat:

Zitat von QuickAndDirty (Beitrag 1524958)
Hätten wir alles in Singletons abgespeichert hätte man die leicht zentral erweitern können dass es jeweils eine Instanz pro thread gibt.

Singletons sind nur hauchdünn marginal besser als globale variablen.
Vor allem wenn sie die shared instance der anderen Singletons nutzen.

Exakt: Singleton ist eigentlich eher ein Antipattern.

Ja, aber hätten wir Singletons anstatt globaler Variablen, dann könnten wir es einfach aufbrechen und in "Ein objekt pro thread" umbauen.

Rollo62 1. Aug 2023 14:17

AW: Lose Funktionen oder als Funktion in Klasse
 
Zitat:

Zitat von freimatz (Beitrag 1525068)
- Die Software ist für eine Maschine. Schon, aber nun hat die Maschine plötzlich meherer Einheite die man steuern muss.
- Orientation? Nun will man die erste Seite quer die anderen längs

Richtig, und wenn es sich mal ändert, dann muss man das halt mit ändern.
Die Änderung geht aber geordnet innerhalb einer Klasse aus meiner Sicht etwas einfacher als bei losen, verstreuten Funktionen.

So gibt es viele "Einheiten" in der Software, welche man über Jahrzehnte nicht anfassen muss.

So eben auch "Device-Orientation", was dann verschiedene Settings unter Portrait und Landscape kapselt.
Das ist z.B. bei mir ein TOrientation-Singleton, für meine Single-Display Projekte, für Tablets und Phones.
Jetzt kommt neuerdings noch "FlipDisplay" und ein rundes "Watch-Display" dazu, das muss halt bei Bedarf erweitert werden,
bleibt aber meiner Meinung immer noch schön unter einem Orientation-Singleton.

Du hast Recht, gibt es auf einmal 5 Device-Displays oder ein ansteckbares USB-Display, dann muss das entsprechend erweitert werden.

Dann verwaltet das Singleton halt auch dynamisch zu mountende USB-Display, es ist aber immer noch ein TDisplays Singleton für mich.
Kann man anders machen, muss man aber nicht.
Ich weiß jedenfalls, dass dieses Singleton wieder Jahrzehnte Bestand haben wird.

Der Vorteil, den ich speziell in diesem Fall sehe ist, dass die Domain "Displays" ein Singleton bleibt, egal wie viele und welche Displays ich noch dazu baue.
Aus meiner Sicht hängen alle Displays eben logisch zusammen und deshalb macht EIN Singleton für mich da immer noch Sinn.
Ein zentraler Verwalter für alle Displays, der könnte verschiedene Funktionen übernehmen.
Das kann er nicht, wenn alle Displays nur separat gesteuert werden.
Zumal die Displays in einer Session nicht tausendmal erzeugt und zerstört werden müssen.

Ok, ist aber meine Philosophie zu dem Thema, generell versuche ich auch Singletons zu vermeiden.
Insbesondere aber bei Hardware-nahen System setze ich das aus o.g. Gründen gerne ein.

freimatz 2. Aug 2023 07:56

AW: Lose Funktionen oder als Funktion in Klasse
 
Zitat:

Zitat von Rollo62 (Beitrag 1525120)
Ok, ist aber meine Philosophie zu dem Thema, generell versuche ich auch Singletons zu vermeiden.
Insbesondere aber bei Hardware-nahen System setze ich das aus o.g. Gründen gerne ein.

:thumb: Wenn man das so wie Du gut durchdacht hat finde ich das auch ok. Meine Intention war, dass man sich überhaupt mal darüber Gedanken macht bevor man das verwendet.


Alle Zeitangaben in WEZ +1. Es ist jetzt 22:44 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz