使用D2009和D2010的新功能在Delphi中创建单例

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)


Joh*_*mas 8

到目前为止,在Delphi 2010中,最好和最安全的方法是使用类构造函数.请参阅此处 - 特别阅读名为Improved encapsulation的段落.

HTH.


Lie*_*ers 7

当我需要单例并且在实现部分中隐藏接口的实现时,我更喜欢使用接口.

好处

  • 程序终止时自动销毁.
  • 没办法意外地创建一个TMySingleton.

缺点

  • 有人可能决定自己实施IMySingleton.

注意:我认为单身人士的使用应该保持在最低限度.总而言之,单身人士只不过是美化的全球变数.如果您开始对代码进行单元测试,那么它们就会变得令人讨厌.

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)


Del*_*ics 4

可以通过重写 Delphi、NewInstanceFreeInstance中的 TRUE 分配器和解除分配器方法来管理此问题。Delphi中的构造函数和析构函数仅分别初始化和终止,它们不分配或释放内存,因此试图隐藏构造函数总是有点误导。

也就是说,只要您覆盖NewInstance,就可以允许自由使用任何和所有构造函数,以便它只返回对该类的单个内存分配的引用。

但尝试在基类中强制执行使用/行为模式是一个错误。并非所有模式都是或需要特定的类来封装该模式。

在这种情况下,您最终会创建一些不必要的复杂内容,而复杂性会在我的经验中引入错误,然后练习的目标就变成试图找到模式实现中的缺陷,然后尝试实施针对这些缺陷的保护措施,而不是继续执行单例类应该执行的实际工作。

记录类的用法要简单得多,也更有效。

例如,作为实现此模式的一种技术,文档对于VCL 中的应用程序屏幕对象来说已经完美地工作了 15 年,更不用说我在那些年里创建的无数其他单例了。