Autor Beitrag
MitschL
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 211

Win 98 SE, Win 2000
D5 Pers, D6 Pers und D7 Pro
BeitragVerfasst: Fr 14.01.05 14:00 
Moin,

das hier Vorgestellte versteht sich eher als Konstrukt für eigene Arbeiten.

Aber worum geht es eigentlich? In größeren Projekten ist es ratsam, auf globale Variablen zu verzichten. Wie aber kann man nun globale Informationen zugänglich machen? Man müßte ja die notwendigen Informationen sonst von Methode zu Methode weiterreichen, was doch ziemlich schnell unübersichtliche Methoden-Köpfe zeigt.

Die Lösung ist eine Klasse, die die globalen Daten hält. Ihre Besonderheit besteht darin, daß man stets mit nur einer Instanz arbeitet, und zwar mit der, die man braucht.

Möglich wird dies durch ein Konstrukt, welches ich erstmal komplett darstellen möchte:
ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
unit UGlobalData;

interface

uses UDataHolder; // Beispiel

type TGlobalData = class( TObject )
  private
    // Ablage interner Daten
    _data: TDataHolder; // Beispiel
    _moreData: String// Beispiel
  public
    // wird vom Create aufgerufen
    class function NewInstance(): TObject; override;
    
    // wird vom Destroy aufgerufen
    procedure FreeInstance; override;
   
   // weitere Methoden
end;

implementation

var
  // die sollten am besten hier stehen
  GGlobalData     : TGlobalData = nil;
  GReferenceCounter: Integer = 0;


class function TGlobalData.NewInstance: TObject;
begin
  if not Assigned( GGlobalData ) then
  begin
    // wird nur einmal angelegt
    GGlobalData := TGlobalData( inherited NewInstance );
    
    // private Variablen anlegen/initialisieren
    GGlobalData._moreData := '';
    GGlobalData._data := TDataHolder.Create();
  end;

  Result := GGlobalData;
  Inc( GReferenceCounter );
end;


procedure TGlobalData.FreeInstance();
begin
  Dec( GReferenceCounter );
  if GReferenceCounter = 0 then
  begin
    GGlobalData := nil;

    // private Variablen freigeben
    _data.Free();
    inherited FreeInstance;
  end;
end;

end.

Abgesehen von den privaten Variablen der Klasse (_data, _moreData) und der genutzten Unit (UDataHolder) kann man den Code so übernehmen und anpassen.
Eine Nutzung sieht folgendermaßen aus:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
var globs: TGlobalData;
//...
begin
  //...
  globs := TGlobalData.Create(); // ganz normal instanziieren
  // globs-Arbeit steht an
  globs.Free(); // gehört dazu
  // ...
end;


Wo auch immer man in den Sourcen auf TGlobalData zugreifen möchte, greift man auf die Instanz zu, die beim ersten Zugriff angelegt wurde. Möglich wird dies durch GGlobalData, welches direkt unter dem Implementation-Abschnitt deklariert wird. Es ist somit nach außen unsichtbar, was einen Zugriff nur über Create() ermöglicht.
Mit jedem Create wird ein Zähler mitgezählt. Durch ihn erfährt man, wieviele Stellen gerade auf TGlobalData zugreifen. Beim Freigeben der letzten Instanz erst wird das Objekt wirklich freigegeben.

Es gibt aber einiges zu beachten:
1. Man sollte diese Klasse nicht veerbbar machen, weil es ja nur eine Instanz- und Referenz-Variable gibt.
2. Private Variablen der TGlobalData-Klasse (meine Beispiele _data und _moreData) sollten nicht im Konstruktor angelegt werden. Ebensowenig sollten sie im Destruktor freigegeben werden. Hier sollten NewInstance und FreeInstance genutzt werden (, wie die Beispiele auch zeigen).
3. Für Die Arbeit mit Multi-Threading sollte man ein Objekt der Klasse so nutzen, wie andere Variablen, die man zwischen den Threads austauscht.

Ich habe die Klasse im Einsatz und konnte noch keine Fehler feststellen, schließe sie aber nicht aus. Wer welche findet, schreit es heraus. :)

Viel Spaß damit und gegrüßt!
MitschL

[edit] DanielG und delfiphan wiesen mich auf teilweise schwere Fehler im ersten Code-Listing hin. Vor die Klassendeklaration gehört natürlich ein type und die NewInstance-Methode nutzt in den Zeilen 38/39 nun das richtige Objekt. Dank an beide.

_________________
"Bloßes Ignorieren ist noch keine Toleranz." (Theodor Fontane)