Two methods of implementing Singleton mode in Delphi
haozi
Abstract This article describes two Delphi implementations of Singleton mode and makes a comparative analysis.
Keyword design pattern, Singleton
The Singleton pattern is a very useful design pattern. Its intent is to simply create an instance of a class and provide a global access point to it. Global variables make an object easily accessible, but do not prevent you from instantiating multiple objects. The purpose of the singleton pattern is to ensure that only one instance exists during the life cycle of the program.
Look at the code below:
PRocedure TForm1.Button1Click(Sender: TObject);
var lS1 : TSingleton; l
S2: TSingleton;
begin
try lS1 := TSingleton.Create; ////Call the constructor of the class
lS2 := TSingleton.Create; ////Call the constructor of the class
//// ...other code
finally
lS1.Free; ////Release the object
lS2.Free; ////Release the object
end;
end;
In the above code, the TSingleton class is instantiated when the Create function is called for the first time. lS1 points to the address of a memory storage object. When the TSingleton.Create function is called for the second time, the TSingleton object is re-instantiated. lS2 points to the memory allocated address. Another address. The Singleton pattern is to let the class itself be responsible for saving its only instance.
In the above code, when ls2 is created, it also points to the object pointed to by ls1 (that is, it is allocated the same memory address). Similarly, we must prevent the memory from being released when releasing ls1, because the single object is also referenced by ls2. This ensures that there is only one class instance during the life cycle of the program.
The sample code of C++ in "Design Pattern" uses C++ static member variables to save instances and uses protected constructor functions. However, since there are no static member variables in Delphi, the method of this singleton mode example cannot be used as is. Below we analyze several ways for DELPHI to implement Singleton mode.
one. Method based on override two Tobject virtual functions
class function NewInstance: TObject; virtual;
procedure FreeInstance; virtual;
The NewInstance function is responsible for allocating memory for the object when the class object is created, while FreeInstance releases the memory on the contrary.
.
The former is called when the object is constructed, and the latter is called when the object is destroyed.
We use two global variables to hold the singleton object and the reference count of the object.
varInstance: TSingleton = nil;
RefCount : Integer = 0;
Units of the TSingleton class:
////------------------------------------------------ --------------------------
////
unit uSingleton;
interface
type
TSingleton = class(TObject)
public
class function NewInstance: TObject; override; ////Override base class function
procedure FreeInstance; override; ////Override base class function
class function RefCount: Integer;////Return the current reference count
end;
//// Declaration global variables
var
Instance: TSingleton = nil;
RefCount: Integer = 0;
implementation
{TSingleton}
procedure TSingleton.FreeInstance;
begin
Dec( RefCount );////Decrease the reference count
if (RefCount = 0) then////Is it 0, if so, release the memory
begin
Instance := nil;
//// Release the private variables of the singleton class
////…
inherited FreeInstance;
end;
end;
class function TSingleton.NewInstance: TObject;
begin
if ( not Assigned( Instance ) ) then
begin
Instance := TSingleton(inherited NewInstance);
////Example of initializing private variables:
//// Instance.VariableName := Value;
end;
Result := Instance ;
Inc( RefCount );
end;
class function TSingleton.RefCount: Integer;
begin
Result := RefCount;
end;
end.
////------------------------------------------------ --------------------------
////
When the constructor of TSingleton is called, our override NewInstance function will be called, and NewInstance will allocate memory and return it to the constructor. In this way, through the override NewInstance function, we ensure that the Create function can only instantiate a TSingleton object (no matter how many times it is called) Create only returns the memory address allocated for the first time). At the same time the RefCount variable holds how many references we have to the object.
Let's look at the test code
procedure TForm1.Button1Click(Sender: TObject);
var
lS1, lS2: TSingleton;
Ob1, Ob2: Tobject;
begin
lS1 := TSingleton.Create;
ShowMessage(IntToStr(RefCount)); //// Ref_Count = 1
lS2 := TSingleton.Create;
ShowMessage(IntToStr(RefCount)); //// Ref_Count = 2
Ob1 := TObject.Create;
Ob2 := Tobject.Create;
if lS1 = lS2 then
ShowMessage('Addresses are equal') //// lS1 = lS2
else
ShowMessage('Addresses are not equal');
if Ob1 = Ob2 then
ShowMessage('Addresses are equal')
else
ShowMessage('Addresses are not equal'); //// Ob1 <> Ob2
end;
When the program calls the destructor (that is, when the FREE function is called), the destructor will call the FreeInstance function to release the memory allocated by the constructor. Override's FreeInstance function ensures that the memory of the singleton mode object is released only when the reference count reaches zero.
Here is our test code:
var
lS1: TSingleton;
lS2: TSingleton;
begin
try
lS1 := TSingleton.Create; ////Call the constructor of the class
lS2 := TSingleton.Create; ////Call the constructor of the class
//// ...other code
finally
lS1.Free; ////Here we will first call the FreeInstance defined by our override,
////Since RefCount is 1 after decrementing 1 at this time, the singleton object has not been released.
lS2.Free; ////dec(RefCount)= 0 releases the singleton object
end;
end;
The above singleton pattern implementation method is a good way to realize that the class itself is responsible for saving its own unique instance (by intercepting the request to create a new object - refer to "Design Patterns". It has no special restrictions on the use of the TSingleton class - —Programmers can call the Create and Free functions at will.
The disadvantage of this mode is that the TSingleton class cannot be inherited as a parent class to generate subclasses. If inheritance generates two subclasses, only one object will be generated during Create.
procedure TForm1.Button1Click(Sender: TObject);
var
lS1: subcategory one;
lS2: subcategory two;
begin
lS1 := Subclass 1.Create;
lS2 := Subclass 2.Create; ////Subclass 2 will not be created, lS2 will point to the memory pointed to by lS1,
////That is, lS1 = lS2end;
two. Delphi implementation of the example in "Design Patterns"
The implementation example of "Design Pattern" is to control only one object instance through a private constructor function. However, the given C++ code implementation does not give how the object is released. The Create function cannot be privatized in Delphi. We define a new function to replace the Create function and shield the Create function of the parent class. The code is as follows
:
////------------------------------------------------ --------------------------
////
unit uSingletonUnit;
interface
uses
Classes, SysUtils;
type
TCSingleton = class(TComponent) ////Inherited from Tcomponent class.
private
constructor CreateInstance(AOwner: TComponent); ////Pass the Owner parameter
//// In this way, the TCSingleton class object will be destroyed together with the Owner (the owner is responsible for destroying the TCSingleton object)
public
constructor Create(AOwner: TComponent); override;
class function Instance(AOwner: TComponent): TCSingleton;
end;
var
gCSingleton: TCSingleton; //// Global variables
implementation
{TCSingleton}
constructor TCSingleton.Create(AOwner: TComponent);
begin
////Shield the function of Create function
raise Exception.CreateFmt('access class %s through Instance only',
[ClassName]);
end;
constructor TCSingleton.CreateInstance(AOwner: TComponent);
begin
////The newly defined constructor is Private
inherited Create(AOwner);
end;
class function TCSingleton.Instance(AOwner: TComponent): TCSingleton;
begin
if not Assigned(gCSingleton) then
gCSingleton := TCSingleton.CreateInstance(AOwner);
Result := gCSingleton;
end;
end.
////------------------------------------------------ ----------------------------/
/
During the use of the above implementation class, programmers do not need to consider the destruction of singleton mode objects. It just cannot call Create, you must call the Instance function to obtain an instance of the object, and pass the singleton owner as a parameter to the function. This implementation method can be inherited as a base class and used in a singleton of the state pattern (see Reference 4) to achieve execution-time polymorphism.
three. Conclusion
The Delphi implementation of Singleton mode can also be found on the Internet. There are other implementation methods. The two methods in this article are
The most common and simple. At the same time, the ideas of other methods are also very similar to the above two methods.