Singleton(单件)模式是一种很有用的设计模式。它的意图的是:仅仅创建一个类的实例,并提供一个访问它的全局访问点。全局变量使的一个对象易被访问,但不能防止你实例化多个对象。单件模式的目的就是确保在程序的生命周期内只有一个实例存在。
多实例实现了数据的并行访问,而单例模式是把它变成串行访问。所以这模式的用途非常的广泛,如处理数据库连接的访问等等。
Delphi实现Singleton也有许多方案,在此列出三种。
方案一:Java或者C++中,类中的变量可以修饰为Static表示该变量不依赖于类的实例而单独存在,在Delphi中没有类似的关键字,所以可以定义一个单元的私有变量来实现对类实例的引用计数。以下代码就是用的此种方案:
unit Unit2;
interface
type
TSingleton = class
public
class function NewInstance: TObject; override;
procedure FreeInstance; override;
class function RefCount: Integer;
end;
implementation
var
Instance : TSingleton = nil;
Ref_Count : Integer = 0;
procedure TSingleton.FreeInstance;
begin
Dec( Ref_Count );
if ( Ref_Count = 0 ) then
begin
Instance := nil;
// Destroy private variables here
inherited FreeInstance;
end;
end;
class function TSingleton.NewInstance: TObject;
begin
if ( not Assigned( Instance ) ) then
begin
Instance := TSingleton(inherited NewInstance);
// Initialize private variables here, like this:
// TSingleton(Result).Variable := Value;
end;
Result := Instance;
Inc(Ref_Count);
end;
class function TSingleton.RefCount: Integer;
begin
Result := Ref_Count;
end;
Instance需要声明在Implementation部分,而不要声明在单元Interface部分,这样变量对单元外的用户是不可见的,这样可以保护变量不会被用户误修改。
可以用下面的代码看到测试的效果,你可加上Pointer将它们的地址打印出来,就更加直观了!
procedure TForm1.Button1Click(Sender: TObject);
var
sing1, sing2: TSingleton;
obj1, obj2: TObject;
begin
sing1:=TSingleton.Create;
sing1.Create;
showmessage(IntToStr(sing1.RefCount));
sing2:=TSingleton.Create;
sing2.Create;
showmessage(IntToStr(sing2.RefCount));
if sing1=sing2 then
showmessage('同地址!');
else
showmessage('不同地址!');
sing1.Free;
sing2.Free;
//
obj1:=TObject.Create;
obj2:=TObject.Create;
if obj1=obj2 then
showmessage('同地址!')
else
showmessage('不同地址!');
obj1.Free;
obj2.Free;
end;
这种方法是根据Delphi的对象机制作的处理。可以像多例类一样使用Create和Free方法来使用它!但是这种方法有一个缺点,就是这种Singleton类不能有多个子类!因为它只会创建一个。
参考: Creating a real singleton class in Delphi 5 by Lasse
方案二:此种方法是White Ants提供的,代码如下:
unit Singleton2;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
type
TSingleton2 = class (TObject)
private
protected
constructor CreateInstance;
class function AccessInstance(Request: Integer): TSingleton2;
procedure SetTestValue(Value: Integer);
public
constructor Create;
destructor Destroy; override;
class function Instance: TSingleton2;
class procedure ReleaseInstance;
end;
implementation
{$J+}
{
********************************** TSingleton2 **********************************
}
constructor TSingleton2.Create;
begin
inherited Create;
raise Exception.CreateFmt('只能通过Instance方法来创建和访问%s的实例!',
[ClassName]);
end;
constructor TSingleton2.CreateInstance;
begin
inherited Create;
end;
destructor TSingleton2.Destroy;
begin
if AccessInstance(0) = Self then AccessInstance(2);
inherited Destroy;
end;
class function TSingleton2.AccessInstance(Request: Integer): TSingleton2;
const
FInstance: TSingleton2 = nil;
begin
{
AccessInstance(0):不作任何处理,供释放实例对象时使用。
AccessInstance(1):存在该实例时直接使用,不存在时则创建该实例。
AccessInstance(2):返回一个空指针,用于重新设置实例。
}
case Request of
0 : ;
1 : if not Assigned(FInstance) then
begin
FInstance := CreateInstance;
FInstance.FConnected:=false;
end;
2 : FInstance := nil;
else
raise Exception.CreateFmt(' %d 是AccessInstance()中的非法调用参数。',
[Request]);
end;
Result := FInstance;
end;
class function TSingleton2.Instance: TSingleton2;
begin
//返回实例
Result := AccessInstance(1);
end;
class procedure TSingleton2.ReleaseInstance;
begin
AccessInstance(0).Free;
end;
end.
这种方案最关键的地方在于类方法AccessInstance中有一个Singleton常量!
参考:Thinking in patterns with Delphi by Liuyi
方案三:这是Delphi2006中生成的Singleton版本,由于对Delphi语言的增强,实现Singleton变得和Java与C++一样的简单!还是看看代码:
TSingleton = class
strict private
constructor Create;
class var
FInstance:TSingleton;
public
class function GetInstance: TSingleton;
procedure FreeInstance;
end;
implementation
constructor TSingleton.Create;
begin
inherited;
end;
class function TSingleton.GetInstance: TSingleton;
begin
If FInstance = nil Then
begin
FInstance := TSingleton.Create();
end;
Result := FInstance;
end;
procedure TSingleton.FreeInstance;
begin
FInstance:=nil;
end;