Singleton pattern
From Free Pascal wiki
Jump to navigationJump to search
{
Implements singleton pattern by using FreePascal's property feature.
A global block can declare properties, just as they could be defined
in a class. The difference is that the global property does not need
a class instance: there is only one instance of this property.
In this case the actual singleton is hidden behind a local writeable
const. This is actually stored in global space, but only accessible from
within its local constant. Since the property has only read access it
makes that it can only be accessed read-only: a singleton.
You must make sure the underlying class is free'd.
That is no problem, because you can call free on the property.
important note:
Invoke<T> returns a lazily-created singleton instance for type T.
The instance is intended to live for the lifetime of the process.
Callers must not free the returned object unless at process end.
It is owned by this unit/runtime pattern and is released only at
process shutdown, or intentionally leaked when relying on OS cleanup.
E,g. for services daemons, let the OS clean up and never call free except
just before process end.
This helper is not meant for scoped, resettable, or short-lived instances.
That would be contrary to the true meaning of a singleton.
If not agreed, blame Thaddy
}
unit singletons;
{$mode objfpc}{$H+}
interface
uses
classes, syncobjs;
var
cs:TCriticalSection;
generic function Invoke<T:class>:T;
implementation
generic function Invoke<T>: T;
{$push}{$J+}const value: T = nil;{$pop}
begin
cs.Acquire;
try
if value = nil then
value := T.Create;
Result := value;
finally
cs.Release;
end;
end;
initialization
cs := TCriticalSection.Create;
finalization
cs.free;
end.
And demos:
program simplesingleton;
{$mode objfpc}
uses classes,singletons;
function getstringlist:TStringlist;
begin
Result := specialize Invoke<TStringlist>;
end;
property SingletonList:TStringlist read getstringlist;
begin
Singletonlist.add('Some text');
writeln(Singletonlist.text);
Singletonlist.free;
end.
program SingletonExample;
{$mode objfpc}{$H+}
uses
SysUtils, Singletons;
type
TSettings = class
private
FName: String;
FCounter: Integer;
public
constructor Create;
procedure Touch;
property Name: String read FName write FName;
property Counter: Integer read FCounter;
end;
constructor TSettings.Create;
begin
inherited Create;
FName := 'default settings';
FCounter := 0;
Writeln('TSettings.Create called');
end;
procedure TSettings.Touch;
begin
Inc(FCounter);
end;
function GetSettings: TSettings;
begin
Result := specialize Invoke<TSettings>;
end;
property Settings: TSettings read GetSettings;
begin
Settings.Name := 'app settings';
Settings.Touch;
Settings.Touch;
Writeln(Settings.Name);
Writeln('Counter: ', Settings.Counter);
if Settings = GetSettings then
Writeln('Same instance');
Settings.free;
end.