Zwei Methoden zur Implementierung des Singleton-Modus in Delphi
haozi
Zusammenfassung Dieser Artikel beschreibt zwei Delphi-Implementierungen des Singleton-Modus und führt eine vergleichende Analyse durch.
Schlüsselwort-Designmuster, Singleton
Das Singleton-Muster ist ein sehr nützliches Designmuster. Sein Zweck besteht einfach darin, eine Instanz einer Klasse zu erstellen und einen globalen Zugriffspunkt darauf bereitzustellen. Globale Variablen erleichtern den Zugriff auf ein Objekt, hindern Sie jedoch nicht daran, mehrere Objekte zu instanziieren. Der Zweck des Singleton-Musters besteht darin, sicherzustellen, dass während des Lebenszyklus des Programms nur eine Instanz vorhanden ist.
Schauen Sie sich den folgenden Code an:
PROzedur TForm1.Button1Click(Sender: TObject);
var lS1 : TSingleton; l
S2: TSingleton;
beginnen
try lS1 := TSingleton.Create; ////Rufen Sie den Konstruktor der Klasse auf
lS2 := TSingleton.Create; ////Rufen Sie den Konstruktor der Klasse auf
//// ...anderer Code
Endlich
lS1.Free; ////Das Objekt freigeben
lS2.Free; ////Das Objekt freigeben
Ende;
Ende;
Im obigen Code wird die TSingleton-Klasse instanziiert, wenn die Create-Funktion zum ersten Mal aufgerufen wird. lS1 zeigt auf die Adresse eines Speicherobjekts. Wenn die TSingleton.Create-Funktion zum zweiten Mal aufgerufen wird, wird das TSingleton-Objekt re -instanziiert. lS2 zeigt auf die dem Speicher zugewiesene Adresse. Das Singleton-Muster besteht darin, die Klasse selbst für das Speichern ihrer einzigen Instanz verantwortlich zu machen.
Wenn ls2 im obigen Code erstellt wird, zeigt es auch auf das Objekt, auf das ls1 zeigt (das heißt, ihm wird dieselbe Speicheradresse zugewiesen. Ebenso müssen wir verhindern, dass der Speicher freigegeben wird, wenn ls1 freigegeben wird, da dies der Fall ist). Das Objekt wird auch von ls2 referenziert. Dadurch wird sichergestellt, dass es während des Lebenszyklus des Programms nur eine Klasseninstanz gibt.
Der Beispielcode von C++ in „Design Pattern“ verwendet statische C++-Membervariablen zum Speichern von Instanzen und verwendet geschützte Konstruktorfunktionen. Da es in Delphi jedoch keine statischen Mitgliedsvariablen gibt, kann die Methode dieses Singleton-Modus-Beispiels nicht unverändert verwendet werden. Im Folgenden analysieren wir verschiedene Möglichkeiten für DELPHI, den Singleton-Modus zu implementieren.
eins. Methode basierend auf dem Überschreiben zweier virtueller Tobject-Funktionen
Klassenfunktion NewInstance: TObject virtual;
Prozedur FreeInstance virtuell;
Die NewInstance-Funktion ist für die Zuweisung von Speicher für das Objekt verantwortlich, wenn das Klassenobjekt erstellt wird, während FreeInstance den Speicher im Gegenteil freigibt.
.
Ersteres wird aufgerufen, wenn das Objekt erstellt wird, und letzteres wird aufgerufen, wenn das Objekt zerstört wird.
Wir verwenden zwei globale Variablen, um das Singleton-Objekt und den Referenzzähler des Objekts zu speichern.
varInstance: TSingleton = nil;
RefCount: Ganzzahl = 0;
Einheiten der TSingleton-Klasse:
////------------------------------- -- --------------------------
////
Einheit uSingleton;
Schnittstelle
Typ
TSingleton = Klasse(TObject)
öffentlich
Klassenfunktion NewInstance: TObject; override; ////Basisklassenfunktion überschreiben
procedure FreeInstance; override; ////Basisklassenfunktion überschreiben
Klassenfunktion RefCount: Integer;////Gibt den aktuellen Referenzzähler zurück
Ende;
//// Deklaration globaler Variablen
var
Instanz: TSingleton = nil;
RefCount: Ganzzahl = 0;
Durchführung
{TSingleton}
Prozedur TSingleton.FreeInstance;
beginnen
Dec( RefCount );////Verringern Sie den Referenzzähler
if (RefCount = 0) then////Ist es 0, wenn ja, den Speicher freigeben
beginnen
Instanz := nil;
//// Geben Sie die privaten Variablen der Singleton-Klasse frei
////…
geerbte FreeInstance;
Ende;
Ende;
Klassenfunktion TSingleton.NewInstance: TObject;
beginnen
if ( not Assigned( Instance ) ) then
beginnen
Instanz := TSingleton(inherited NewInstance);
////Beispiel für die Initialisierung privater Variablen:
//// Instance.VariableName := Value;
Ende;
Ergebnis := Instanz ;
Inc( RefCount );
Ende;
Klassenfunktion TSingleton.RefCount: Integer;
beginnen
Ergebnis := RefCount;
Ende;
Ende.
////------------------------------- -- --------------------------
////
Wenn der Konstruktor von TSingleton aufgerufen wird, wird unsere Override-NewInstance-Funktion aufgerufen, und NewInstance reserviert Speicher und gibt ihn an den Konstruktor zurück. Auf diese Weise stellen wir durch die Override-NewInstance-Funktion sicher, dass die Create-Funktion nur ein TSingleton-Objekt instanziieren kann (egal wie oft es aufgerufen wird. Create gibt nur die beim ersten Mal zugewiesene Speicheradresse zurück.) Gleichzeitig speichert die Variable RefCount, wie viele Referenzen wir auf das Objekt haben.
Schauen wir uns den Testcode an
procedure TForm1.Button1Click(Sender: TObject);
var
lS1, lS2: TSingleton;
Ob1, Ob2: Objekt;
beginnen
lS1 := TSingleton.Create;
ShowMessage(IntToStr(RefCount)); //// Ref_Count = 1
lS2 := TSingleton.Create;
ShowMessage(IntToStr(RefCount)); //// Ref_Count = 2
Ob1 := TObject.Create;
Ob2 := Tobject.Create;
wenn lS1 = lS2 dann
ShowMessage('Adressen sind gleich') //// lS1 = lS2
anders
ShowMessage('Adressen sind nicht gleich');
wenn Ob1 = Ob2 dann
ShowMessage('Adressen sind gleich')
anders
ShowMessage('Adressen sind nicht gleich'); //// Ob1 <> Ob2
Ende;
Wenn das Programm den Destruktor aufruft (dh wenn die FREE-Funktion aufgerufen wird), ruft der Destruktor die FreeInstance-Funktion auf, um den vom Konstruktor zugewiesenen Speicher freizugeben. Die FreeInstance-Funktion von Override stellt sicher, dass der Speicher des Singleton-Modus-Objekts nur dann freigegeben wird, wenn der Referenzzähler Null erreicht.
Hier ist unser Testcode:
var
lS1: TSingleton;
lS2: TSingleton;
beginnen
versuchen
lS1 := TSingleton.Create; ////Rufen Sie den Konstruktor der Klasse auf
lS2 := TSingleton.Create; ////Rufen Sie den Konstruktor der Klasse auf
//// ...anderer Code
Endlich
lS1.Free; ////Hier rufen wir zuerst die durch unsere Überschreibung definierte FreeInstance auf.
////Da RefCount nach dem Dekrementieren um 1 zu diesem Zeitpunkt 1 ist, wurde das Singleton-Objekt nicht freigegeben.
lS2.Free; ////dec(RefCount)= 0 gibt das Singleton-Objekt frei
Ende;
Ende;
Die obige Singleton-Muster-Implementierungsmethode ist eine gute Möglichkeit, zu erkennen, dass die Klasse selbst für das Speichern ihrer eigenen eindeutigen Instanz verantwortlich ist (indem sie die Anforderung zum Erstellen eines neuen Objekts abfängt – siehe „Entwurfsmuster“. Es gibt keine besonderen Einschränkungen für die Verwendung der TSingleton-Klasse – Programmierer können die Funktionen Create und Free nach Belieben aufrufen.
Der Nachteil dieses Modus besteht darin, dass die TSingleton-Klasse nicht als übergeordnete Klasse geerbt werden kann, um Unterklassen zu generieren. Wenn durch Vererbung zwei Unterklassen generiert werden, wird beim Erstellen nur ein Objekt generiert.
procedure TForm1.Button1Click(Sender: TObject);
var
lS1: Unterkategorie eins;
lS2: Unterkategorie zwei;
beginnen
lS1 := Unterklasse 1.Create;
lS2 := Subclass 2.Create; //// Subclass 2 wird nicht erstellt, lS2 zeigt auf den Speicher, auf den lS1 zeigt,
////Das heißt, lS1 = lS2end;
zwei. Delphi-Implementierung des Beispiels in „Design Patterns“
Das Implementierungsbeispiel von „Design Pattern“ besteht darin, nur eine Objektinstanz über eine private Konstruktorfunktion zu steuern. Allerdings gibt die angegebene C++-Codeimplementierung nicht an, wie das Objekt freigegeben wird. Die Create-Funktion kann in Delphi nicht privatisiert werden. Wir definieren eine neue Funktion, um die Create-Funktion zu ersetzen und die Create-Funktion der übergeordneten Klasse abzuschirmen. Der Code lautet wie folgt
:
////------------------------------- -- --------------------------
////
Einheit uSingletonUnit;
Schnittstelle
verwendet
Klassen, SysUtils;
Typ
TCSingleton = class(TComponent) ////Von der Tcomponent-Klasse geerbt.
Privat
Konstruktor CreateInstance(AOwner: TComponent); ////Übergeben Sie den Owner-Parameter
//// Auf diese Weise wird das TCSingleton-Klassenobjekt zusammen mit dem Eigentümer zerstört (der Eigentümer ist für die Zerstörung des TCSingleton-Objekts verantwortlich).
öffentlich
Konstruktor Create(AOwner: TComponent);
Klassenfunktion Instance(AOwner: TComponent): TCSingleton;
Ende;
var
gCSingleton: TCSingleton; //// Globale Variablen
Durchführung
{TCSingleton}
Konstruktor TCSingleton.Create(AOwner: TComponent);
beginnen
////Schützen Sie die Funktion der Funktion „Erstellen“.
raise Exception.CreateFmt('Zugriff auf Klasse %s nur über Instanz',
[Klassenname]);
Ende;
Konstruktor TCSingleton.CreateInstance(AOwner: TComponent);
beginnen
////Der neu definierte Konstruktor ist Private
geerbt Create(AOwner);
Ende;
Klassenfunktion TCSingleton.Instance(AOwner: TComponent): TCSingleton;
beginnen
wenn nicht Assigned(gCSingleton), dann
gCSingleton := TCSingleton.CreateInstance(AOwner);
Ergebnis := gCSingleton;
Ende;
Ende.
////------------------------------- -- --------------/
/
Bei der Verwendung der oben genannten Implementierungsklasse müssen Programmierer die Zerstörung von Singleton-Modus-Objekten nicht berücksichtigen. Es kann einfach nicht „Create“ aufgerufen werden. Sie müssen die Instanzfunktion aufrufen, um eine Instanz des Objekts zu erhalten, und den Singleton-Eigentümer als Parameter an die Funktion übergeben. Diese Implementierungsmethode kann als Basisklasse geerbt und in einem Singleton des Zustandsmusters (siehe Referenz 4) verwendet werden, um Polymorphismus zur Ausführungszeit zu erreichen.
drei. Abschluss
Die Delphi-Implementierung des Singleton-Modus ist auch im Internet zu finden. Es gibt zwei Methoden in diesem Artikel
Das gebräuchlichste und einfachste. Gleichzeitig sind auch die Ideen anderer Methoden den beiden oben genannten Methoden sehr ähnlich.