This singleton pattern is based on the singleton pattern as described in [Gam+, pages 127..134]. The Delphi implementation for the pattern originates from White Ants.
Ensure a class only has one instance, and provide a global point of access to it [Gam+,p127].
The motivation is based on [Gam+, page 127]. Its important for some classes to have exactly one instance. Although there can be many printers in a system, there should be only one printer spooler. There should be only one file system (or file system manager) and one window manager.
How do we ensure that a class has only one instance and that the instance is easily accessible? A global variable makes an object accessible, but it doesnt keep you from instantiating multiple objects. Global variables also tend to pollute the name space.
A better solution is to make the class itself responsible for keeping track of its sole instance. The class can ensure that no other instance can be created (by intercepting requests to create new objects), and it can provide a way to access the instance. This is the singleton pattern. Typical use of this pattern is in service like classes.
Consider for example a class TProgressor which could be used as a low level service to deal with time consuming processes. Typical methods would be: StartProgress, EndProgress, Abort and typical properties would be Progress, Aborted etc. The following example shows part of TProgressors interface:
type
TProgressor = class (TObject)
private
FProgress: Integer;
protected
procedure SetProgress(Value: Integer);
public
procedure StartProgress;
property Progress: Integer read FProgress
write SetProgress;
end;
Applying the singleton pattern to this class results in the following interface:
type
TProgressor = class (TObject)
private
FProgress: Integer;
protected
constructor CreateInstance;
class function AccessInstance(Request:
Integer): TProgressor;
procedure SetProgress(Value: Integer);
public
constructor Create;
destructor Destroy; override;
class function Instance: TProgressor;
class procedure ReleaseInstance;
procedure StartProgress;
property Progress: Integer read FProgress
write SetProgress;
end;
Lets have a look at the public interface first: The class function Instance is used to access the single instance of this class. The first time this class method is called, the instance is actually created. The constructor Create is overridden to raise an exception if you attempt to create an instance without using the Instance method. This will prevent you from accidentally creating multiple instances. Calling ReleaseInstance will clean up the single instance if it existed. You would typically call this method in a clean up section. In Delphi 1.0 from an exit procedure, in Delphi 2.0 in a units finalization section. Dont call the TProgressor.Instance.Free to clean up the instance, since this will first create it, if it hadnt been created before. Destroy co-operates in the instance bookkeeping.
Now lets have a look at the actual implementation of this pattern.
constructor TProgressor.Create;
begin
inherited Create;
raise Exception.CreateFmt('Access class %s through
Instance only',
[ClassName]);
end;
constructor TProgressor.CreateInstance;
begin
inherited Create;
end;
destructor TProgressor.Destroy;
begin
if AccessInstance(0) = Self then AccessInstance(2);
inherited Destroy;
end;
class function TProgressor.AccessInstance(Request:
Integer): TProgressor;
const FInstance: TProgressor = nil;
begin
case Request of
0 : ;
1 : if not Assigned(FInstance) then
FInstance := CreateInstance;
2 : FInstance := nil;
else
raise Exception.CreateFmt('Illegal request
%d in AccessInstance',
[Request]);
end;
Result := FInstance;
end;
class function TProgressor.Instance: TProgressor;
begin
Result := AccessInstance(1);
end;
class procedure TProgressor.ReleaseInstance;
begin
AccessInstance(0).Free;
end;
procedure TProgressor.SetProgress(Value: Integer);
begin
{ Place here the implementation of the progress
mechanism ]
end;
procedure TProgressor.StartProgress;
begin
{ Placehere the implementation of the progress
mechanism ]
end;
The secret of this pattern is in the AccessInstance method. This method uses a typed constant to store the instance. We needed to use this work around because Delphi doesnt support (static) class fields in classes. Depending on the parameter Request the method will either return the instance (Request = 0), create an instance (Request = 1), reset the instance to nil (Request =2). Delphi 2.0 allows this use of typed constants only if the $J+ compiler switch is set.
By making the constructor CreateInstance protected, we assure that no other class is able to call this constructor. It may however be made virtual and can be overridden in descendant classes. In that case the first class to call Instance will determine the actual type to be instantiated.
The only code you manually need to add, is the clean up code in the units initialization or finalization section. This could be something like:
unit Progress;
...
...
implementation
...
...
procedure ShutDown;
begin
TProgressor.ReleaseInstance;
end;
finalisation
ShutDown;
end.
You can now use the TProgressor class as follows:
procedure TSomeClass.DoSomething;
var I: Integer;
begin
TProgressor.Instance.StartProgress;
for I := 0 to 100 do
begin
TProgressor.Instance.Progress := I;
..{ do something useful }
end;
TProgressor.Instance.EndProgress;
end;