我想在Delphi中创建一个单例。我在使用旧版本的Delphi之前完成了这个,最后使用全局变量(在实现部分中)并使用初始化和终结来处理实例。此外,无法阻止用户创建实例,因为您无法隐藏标准构造函数。我想知道是否有任何新的功能,如类构造函数和析构函数,以及类变量(好的,不是那么新),也许是泛型,可以帮助创建一个通用的单例类。我还没有设法让我感到满意。
答案 0 :(得分:12)
如果你只需要一个普通的单例,最简单的方法是使用plainstan建议的类构造函数和类方法。但是,如果您需要按需构建的单身人士(即首次访问时),仿制药非常有用。
以下代码来自我的一个实用单位;它基本上为Delphi 2009以后提供了一个通用的单件工厂。
interface
type
{$HINTS OFF}
{ TSingletonInstance<> implements lazy creation, which is sometimes useful for avoiding
expensive initialization operations.
If you do not require lazy creation and you target only Delphi 2010 onwards, you should
use class constructors and class destructors instead to implement singletons. }
TSingletonInstance<T: class, constructor> = record
private
FGuard: IInterface;
FInstance: T;
function GetInstance: T;
function CreateInstance: TObject;
public
property Instance: T read GetInstance;
end;
{$HINTS ON}
TSingletonFactoryFunction = function: TObject of object;
{ Private symbols (which are in the interface section because of known limitations of generics) }
procedure _AllocateSingletonInstance (InstanceRecord: Pointer; Factory: TSingletonFactoryFunction);
implementation
{ TSingleton }
var
SingletonCriticalSection: TRTLCriticalSection;
type
TSingletonGuard = class (TInterfacedObject)
private
FSingletonInstance: TObject;
public
constructor Create (AInstance: TObject);
destructor Destroy; override;
end;
PUntypedSingletonInstance = ^TUntypedSingletonInstance;
TUntypedSingletonInstance = record
FGuard: IInterface;
FInstance: TObject;
end;
// TODO: is a lock required for multiple threads accessing a single interface variable?
procedure _AllocateSingletonInstance (InstanceRecord: Pointer; Factory: TSingletonFactoryFunction);
var
USI: PUntypedSingletonInstance;
begin
USI := PUntypedSingletonInstance (InstanceRecord);
EnterCriticalSection (SingletonCriticalSection);
if USI.FInstance = nil then
begin
USI.FInstance := Factory ();
USI.FGuard := TSingletonGuard.Create (USI.FInstance);
end;
LeaveCriticalSection (SingletonCriticalSection);
end;
constructor TSingletonGuard.Create (AInstance: TObject);
begin
FSingletonInstance := AInstance;
end;
destructor TSingletonGuard.Destroy;
begin
FSingletonInstance.Free;
inherited;
end;
function TSingletonInstance<T>.GetInstance: T;
var
Factory: TSingletonFactoryFunction;
begin
if FInstance = nil then
begin
Factory := Self.CreateInstance; // TODO: associate QC report
_AllocateSingletonInstance (@Self, Factory);
end;
Result := FInstance;
end;
function TSingletonInstance<T>.CreateInstance: TObject;
begin
Result := T.Create;
end;
initialization
InitializeCriticalSection (SingletonCriticalSection);
finalization
DeleteCriticalSection (SingletonCriticalSection);
用法如下:
type
TMySingleton = class
public
constructor Create;
class function Get: TMySingleton; static;
end;
var
MySingletonInstance: TSingletonInstance<TMySingleton>;
class function TMySingleton.Get: TMySingleton;
begin
Result := MySingletonInstance.Instance;
end;
答案 1 :(得分:8)
到目前为止,在Delphi 2010中,最好和最安全的方法是使用类构造函数。请参阅here - 特别阅读名为改进的封装的段落。
HTH。
答案 2 :(得分:7)
我更喜欢在需要单例时使用接口,并在实现部分隐藏接口的实现。
好处
<强>缺点强>
注意:我认为单身人士的使用应该保持在最低限度。总而言之,单身人士只不过是美化的全球变数。如果您开始对代码进行单元测试,那么它们就会变得很麻烦。
unit uSingleton;
interface
type
ISingleton = interface
['{8A449E4B-DEF9-400E-9C21-93DFA2D5F662}']
end;
function Singleton: ISingleton;
implementation
uses
SyncObjs;
type
TSingleton = class(TInterfacedObject, ISingleton);
var
Lock: TCriticalSection;
function Singleton: ISingleton;
const
_singleton: ISingleton = nil;
begin
if not Assigned(_singleton) then
begin
Lock.Acquire;
try
if not Assigned(_singleton) then
_singleton := TSingleton.Create();
finally
Lock.Release;
end;
end;
Result := _singleton;
end;
initialization
Lock := TCriticalSection.Create;
finalization
Lock.Free;
end.
答案 3 :(得分:4)
有一种方法可以隐藏TObject的继承“Create”构造函数。虽然无法更改访问级别,但可以使用另一个具有相同名称的公共无参数方法隐藏它:“创建”。这极大地简化了Singleton类的实现。请参阅代码的简单性:
unit Singleton;
interface
type
TSingleton = class
private
class var _instance: TSingleton;
public
//Global point of access to the unique instance
class function Create: TSingleton;
destructor Destroy; override;
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.
我在原帖中添加了详细信息:http://www.yanniel.info/2010/10/singleton-pattern-delphi.html
答案 4 :(得分:3)
对于单例,您可以覆盖NewInstance方法。并使用类变量。您在第一次调用时创建变量,并将指针返回给彼此调用的类。
你最后需要找到一些东西来销毁它(可能使用finalize)。
答案 5 :(得分:3)
可以通过覆盖Delphi中的TRUE分配器和解除分配器方法, NewInstance 和 FreeInstance 来管理它。 Delphi中的构造函数和析构函数只分别初始化和终结,它们不分配或释放内存,因此试图隐藏构造函数总是有点误导。
即。只要你覆盖 NewInstance ,就可以允许任意和所有构造函数的自由使用,这样它只会返回对类的一个单独内存分配的引用。
但是尝试在基类中强制执行使用/行为模式是一个错误。并非所有模式都是或需要特定的类来封装模式。
在这样的情况下,你最终会创建一些不必要的复杂的东西,并且复杂性会吸引我的经验中的错误,然后练习的对象会试图找到模式实现中的缺陷,然后尝试实施针对这些缺陷的保护措施而不是继续单身类应该执行的实际工作。
记录班级的用法是远远的,更简单,更有效。
作为实现此模式的技术的文档在VCL中的应用程序和屏幕对象已经完美运行了15年,例如,更不用说无数其他单例我在那些年里创造的。
答案 6 :(得分:0)
我更喜欢使用代码生成器创建单例类。泛型的问题在于,所有代码都是在内存中生成的,而不是在源文件中生成的。这会增加调试的难度。