The purpose of this post is NOT to cover the insights and applicability of the Singleton pattern, instead I just pretend to give you a Delphi code snipped that implements it. This implementation is focused in avoiding variables that are global to the unit (or Unit Global Variables).
Fist of all, I am borrowing the definition and UML diagram of the Singleton Design Pattern, as written in the Design Patterns: Elements of Reusable Object-Oriented Software book. I strongly suggest you to read this book if you want to have a deeper understanding of the classic design patterns.
Singleton: “Ensure a class only has one instance, and provide a global point of access to it.”
UML diagram:
For all Delphi versions previous to Delphi 7, the only way to implement this pattern is by using a Unit Global Variable, instead of a class static field. Fortunately, Delphi 7 featured class variables long time ago and this way, we can implement the pattern avoiding global scoped variables. For some reason, I have never seen an implementation of the Singleton pattern in Delphi using class variables, so here is my own:
unit Singleton;
interface
type
TSingleton = class
private
//Private fields and methods here...
(****************************************************)
(*** Class variables were introduced in Delphi 7. ***)
(*** Older Delphi versions should implement ***)
(*** this field as a unit global variable ***)
class var _instance: TSingleton;
(****************************************************)
protected
(*** Constructor is protected!!! ***)
constructor Create;
//Other protected methods here...
public
destructor Destroy; override;
(********************************************)
(*** Client code should use this function ***)
(***
("Instance"),
instead of the
***)
(***
"constructor"
to create (or access) ***)
(*** a singleton instance. ***)
class function Instance: TSingleton;
(********************************************)
class function NewInstance: TObject; override;
//Other public methods and properties here...
end;
implementation
{ TSingleton }
constructor TSingleton.Create;
begin
inherited Create;
end;
destructor TSingleton.Destroy;
begin
_instance:= nil;
inherited;
end;
class function TSingleton.Instance: TSingleton;
begin
if (_instance = nil) then
_instance:= TSingleton.Create;
result:= _instance;
end;
class function TSingleton.NewInstance: TObject;
begin
if (_instance = nil) then
_instance:= inherited NewInstance as Self;
result:= _instance;
end;
end.
There was a problem with my original implementation. In Delphi:
- Constructors are inherited (this doesn't happen in C++, Java, C#, etc.)
- All classes inherit from TObject ultimately.
I traduced Ali’s words into the source code above. The
Rethinking over this once again:
The implementation above ensures the class only has one instance; but it does not provide a global point of access to it. Instead of that, we now have two points of access. Take a look at the following code snipped:
//Point of access ONE
SingletonObject1:= TSingleton.Create;
//Point of access TWO
SingletonObject2:= TSingleton.Instance;
Yes, two point of access: TSingleton.Create and TSingleton.Instance.
Rethinking this many times, I figured out the simplest implementation. We don’t even need to deal with the evil NewInstance class function. It looks like this:
unit Singleton;
interface
type
TSingleton = class
private
//Private fields and methods here...
class var _instance: TSingleton;
protected
//Other protected methods here...
public
//Global point of access to the unique instance
class function Create: TSingleton;
destructor Destroy; override;
//Other public methods and properties here...
end;
implementation
{ TSingleton }
class function TSingleton.Create: TSingleton;
begin
if (_instance = nil) then
_instance:= inherited Create as Self;
result:= _instance;
end;
destructor TSingleton.Destroy;
begin
_instance:= nil;
inherited;
end;
end.
The only point of access to the singleton instance is the class function “Create”, which hides the TObject.Create parameterless constructor (more details here). Now we have only one instance and a global point of access to it.
The comments inside the above code are self-explanatory. Furthermore, for those stuck with older Delphi versions, the Singleton pattern should be implemented as follows:
unit Singleton;
interface
type
TSingleton = class
private
//Private fields and methods here...
protected
//Other protected methods here...
public
//Global point of access to the unique instance
class function Create: TSingleton;
destructor Destroy; override;
//Other public methods and properties here...
end;
var
_instance: TSingleton; //Unit Global Variable.
implementation
...................
As a conclusion, I would suggest avoiding unit global variables as much as possible; in fact, as a rule of thumb, I would suggest to make your variables' scope as local as possible. This applies not only to this example (implementation of the Singleton Design Pattern), but to programming in general. Furthermore, the class variables in Delphi are equivalent to static member variables in C++, Java, C#...Class variables were introduced in Delphi 7; so, previous versions should use global variables to implement a Singleton class.
Note:
There are three books about Design Patterns I would suggest you to read: