![]() |
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:
oder so
interface
function HandleWorkflow1: Boolean; function HandleWorkflow2: Boolean; function HandleWorkflow3: Boolean; function HandleWorkflow4: Boolean; var Variable1..3:Integer; implementation ....
Delphi-Quellcode:
Ich habe dazu den Beitrag
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 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 |
AW: Lose Funktionen oder als Funktion in Klasse
Zitat:
Zitat:
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. |
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:
oder
public
Variable1 : Integer;
Delphi-Quellcode:
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.
private
FVariable1 : Integer; public property Variable1 : Integer read FVariable1 write FVariable1; // bzw. mit Getter und/oder Setter 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:
So wie diese perversen Form-Vaiablen, welche Delphi standardmäßig bereitstellt.
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; Wenn es "nur" als einzelne Kapselung dienenn soll, dann Class-Function und Class-Property, sowie Class-Var
Delphi-Quellcode:
end;
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 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; |
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:
Beim Zugriff muss dann immer ein
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;
Delphi-Quellcode:
davor gestellt werden.
TWorkFlow.
Alternativ geht auch ein record:
Delphi-Quellcode:
Wenn man dann noch die Redundanz in den Methodennamen (in der Praxis wohl dann auch bei den Variablen) eliminiert, kommt sowas raus:
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;
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; |
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:
erschließt sich mir nicht ganz. Ich hätte gesagt, dass es letztlich das gleiche ist wie ich es mit initialize und finalization mache
class function HandleWorkflow1: Boolean;
Ich habe jahrelang so eine Klasse wie oben - aber nur mit procedure udn function - ohne Create und Free benutzt. Ohne Probleme. Grüße Gerd |
AW: Lose Funktionen oder als Funktion in Klasse
Zitat:
|
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? |
AW: Lose Funktionen oder als Funktion in Klasse
Nicht, dass ich wüsste. Aber wenn man etwas mehr tippt:
Delphi-Quellcode:
dann wird auch kein Setter angelegt.
...
property Blubb: integer read FBlubb write FBlubb; |
AW: Lose Funktionen oder als Funktion in Klasse
Oder man verwendet das propf Template (oder MMX).
|
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. |
AW: Lose Funktionen oder als Funktion in Klasse
Zitat:
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 |
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. |
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. |
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.). |
AW: Lose Funktionen oder als Funktion in Klasse
Zitat:
Zum Warum gibt es im Netz vieles zu finden z.B.: ![]() In der Regel reicht eine einzigste eigene globale Variable für eine grössere Delphi-Anwendung. |
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. |
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. |
AW: Lose Funktionen oder als Funktion in Klasse
Zitat:
Vor allem wenn sie die shared instance der anderen Singletons nutzen. |
AW: Lose Funktionen oder als Funktion in Klasse
Zitat:
|
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. |
AW: Lose Funktionen oder als Funktion in Klasse
Zitat:
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. |
AW: Lose Funktionen oder als Funktion in Klasse
Zitat:
Ich arbeite dazu hiermit: ![]() 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. |
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:
![]() Wenn das nicht ginge, könnte man die Singleton-Klassen ja gleich als
Delphi-Quellcode:
deklarieren.
sealed
|
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. |
AW: Lose Funktionen oder als Funktion in Klasse
Zitat:
- 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. |
AW: Lose Funktionen oder als Funktion in Klasse
Zitat:
|
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. |
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:
Der Zugriff erfolgt dann über entsprechende Methoden wie z.B. diese:
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.
Delphi-Quellcode:
oder je nach Geschmack auch als Klassenmethode:
function TranslationManager: TTranslationManager;
begin result := TSingleton<TTranslationManager>.Instance; end;
Delphi-Quellcode:
Letzteres verdeutlicht die Singleton-Eigenschaft schon im Namen, aber das ist vielleicht nicht immer erwünscht.
class function TTranslationManager.Singleton: TTranslationManager;
begin Result := TSingleton<TTranslationManager>.Instance; end; |
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. |
AW: Lose Funktionen oder als Funktion in Klasse
Zitat:
|
AW: Lose Funktionen oder als Funktion in Klasse
Zitat:
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. |
AW: Lose Funktionen oder als Funktion in Klasse
Zitat:
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 15:23 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz