Ste*_*eve 10 delphi singleton design-patterns
我想在Delphi中创建一个单例.我在使用旧版本的Delphi之前完成了这个,最后使用全局变量(在实现部分中)并使用初始化和终结来处理实例.此外,无法阻止用户创建实例,因为您无法隐藏标准构造函数.我想知道是否有任何新的功能,如类构造函数和析构函数,以及类变量(好的,不是那么新),也许是泛型,可以帮助创建一个通用的单例类.我还没有设法让我感到满意.
Mor*_*tel 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);
Run Code Online (Sandbox Code Playgroud)
用法如下:
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;
Run Code Online (Sandbox Code Playgroud)
当我需要单例并且在实现部分中隐藏接口的实现时,我更喜欢使用接口.
好处
缺点
注意:我认为单身人士的使用应该保持在最低限度.总而言之,单身人士只不过是美化的全球变数.如果您开始对代码进行单元测试,那么它们就会变得令人讨厌.
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.
Run Code Online (Sandbox Code Playgroud)
可以通过重写 Delphi、NewInstance和FreeInstance中的 TRUE 分配器和解除分配器方法来管理此问题。Delphi中的构造函数和析构函数仅分别初始化和终止,它们不分配或释放内存,因此试图隐藏构造函数总是有点误导。
也就是说,只要您覆盖NewInstance,就可以允许自由使用任何和所有构造函数,以便它只返回对该类的单个内存分配的引用。
但尝试在基类中强制执行使用/行为模式是一个错误。并非所有模式都是或需要特定的类来封装该模式。
在这种情况下,您最终会创建一些不必要的复杂内容,而复杂性会在我的经验中引入错误,然后练习的目标就变成试图找到模式实现中的缺陷,然后尝试实施针对这些缺陷的保护措施,而不是继续执行单例类应该执行的实际工作。
记录类的用法要简单得多,也更有效。
例如,作为实现此模式的一种技术,文档对于VCL 中的应用程序和屏幕对象来说已经完美地工作了 15 年,更不用说我在那些年里创建的无数其他单例了。