シングルトンのインスタンスは外部から破棄可能だけどいいの?(意訳)というご意見を高橋さんから頂きました。確かにどの実装も、取得したインスタンスに対してFree(あるいはDestroy)を行うことで解放できてしまいます。
シングルトンに求められる条件を考えれば、インスタンスを破棄された後でアクセスされたときはインスタンスを再度生成する("Phoenix Singleton")か、外部からは破棄できないようにするか、いずれかが望ましいと考えられます。
まず再生成する場合です。こちらはインスタンスの破棄時にインスタンスへの参照を初期化することで、次回参照時にインスタンスを再度生成します。initialization/finalization版から。
unit Unit20;
interface
uses
SysUtils;
type
TSingleton = class(TObject)
private
FTestValue: Integer;
constructor CreateInstance;
public
constructor Create;
destructor Destroy; override;
class function GetInstance: TSingleton;
property TestValue: Integer
read FTestValue
write FTestValue;
end;
ECreateSingleton = class(Exception)
end;
implementation
var
FSingleton: TSingleton;
{ TSingleton }
constructor TSingleton.Create;
begin
raise ECreateSingleton.Create('TSingleton.Create cannot use.');
end;
constructor TSingleton.CreateInstance;
begin
inherited Create;
{ Initialize }
FTestValue := 0;
end;
destructor TSingleton.Destroy;
begin
{ Delete singleton reference }
FSingleton := nil;
{ Finalize }
inherited;
end;
class function TSingleton.GetInstance: TSingleton;
begin
if FSingleton = nil then
begin
FSingleton := TSingleton.CreateInstance;
end;
Result := FSingleton;
end;
initialization
FSingleton := nil;
finalization
FSingleton.Free;
end.
再生成する場合のclass constructor/class destructor版です。unit Unit22;
interface
uses
SysUtils;
type
TSingleton = class(TObject)
private
FTestValue: Integer;
class var
FSingleton: TSingleton;
constructor CreateInstance;
public
class constructor Create;
class destructor Destroy;
constructor Create;
destructor Destroy; override;
class function GetInstance: TSingleton;
property TestValue: Integer
read FTestValue
write FTestValue;
end;
ECreateSingleton = class(Exception)
end;
implementation
{ TSingleton }
class constructor TSingleton.Create;
begin
FSingleton := nil;
end;
class destructor TSingleton.Destroy;
begin
FSingleton.Free;
end;
constructor TSingleton.Create;
begin
raise ECreateSingleton.Create('TSingleton.Create cannot use.');
end;
constructor TSingleton.CreateInstance;
begin
inherited Create;
{ Initialize }
FTestValue := 0;
end;
destructor TSingleton.Destroy;
begin
{ Delete singleton reference }
FSingleton := nil;
{ Finalize }
inherited;
end;
class function TSingleton.GetInstance: TSingleton;
begin
if FSingleton = nil then
begin
FSingleton := TSingleton.CreateInstance;
end;
Result := FSingleton;
end;
end.
次に外部からの破棄を禁止する場合です。destructor Destroy (ja)もconstructor Create (ja)と同様にTObjectでpublicとされていてスコープを狭化できないため、例外を送出することでインスタンスの破棄をブロックしています(コンストラクタと違いドキュメントなどで明示されていませんが、逆アセンブル表示で見る限りデストラクタもまた例外の送出で処理をブロックできると考えます)。ただし通常のコンストラクタ呼び出しからデストラクタが呼ばれる場合と、終了時にデストラクタが呼ばれる場合はフラグで区別して通常の処理を行います。こちらもinitialization/finalization版から。
unit Unit24;
interface
uses
SysUtils;
type
TSingleton = class(TObject)
private
FTestValue: Integer;
constructor CreateInstance;
public
constructor Create;
destructor Destroy; override;
class function GetInstance: TSingleton;
property TestValue: Integer
read FTestValue
write FTestValue;
end;
ECreateSingleton = class(Exception)
end;
EDestroySingleton = class(Exception)
end;
implementation
var
FSingleton: TSingleton;
FInternalDestroy: Boolean;
{ TSingleton }
constructor TSingleton.Create;
begin
FInternalDestroy := True;
raise ECreateSingleton.Create('TSingleton.Create cannot use.');
end;
constructor TSingleton.CreateInstance;
begin
inherited Create;
{ Initialize }
FTestValue := 0;
end;
destructor TSingleton.Destroy;
begin
if FInternalDestroy = False then
begin
raise EDestroySingleton.Create('TSingleton.Destroy cannnot use.');
end;
FInternalDestroy := False;
{ Finalize }
inherited;
end;
class function TSingleton.GetInstance: TSingleton;
begin
if FSingleton = nil then
begin
FSingleton := TSingleton.CreateInstance;
end;
Result := FSingleton;
end;
initialization
FSingleton := nil;
finalization
FInternalDestroy := True;
FSingleton.Free;
end.
最後に外部から破棄を禁止する場合のclass constructor/class destructor版です。unit Unit26;
interface
uses
SysUtils;
type
TSingleton = class(TObject)
private
FTestValue: Integer;
class var
FSingleton: TSingleton;
FInternalDestroy: Boolean;
constructor CreateInstance;
public
class constructor Create;
class destructor Destroy;
constructor Create;
destructor Destroy; override;
class function GetInstance: TSingleton;
property TestValue: Integer
read FTestValue
write FTestValue;
end;
ECreateSingleton = class(Exception)
end;
EDestroySingleton = class(Exception)
end;
implementation
{ TSingleton }
class constructor TSingleton.Create;
begin
FSingleton := nil;
end;
class destructor TSingleton.Destroy;
begin
FInternalDestroy := True;
FSingleton.Free;
end;
constructor TSingleton.Create;
begin
FInternalDestroy := True;
raise ECreateSingleton.Create('TSingleton.Create cannot use.');
end;
constructor TSingleton.CreateInstance;
begin
inherited Create;
{ Initialize }
FTestValue := 0;
end;
destructor TSingleton.Destroy;
begin
if FInternalDestroy = False then
begin
raise EDestroySingleton.Create('TSingleton.Destroy cannnot use.');
end;
FInternalDestroy := False;
{ Finalize }
inherited Destroy;
end;
class function TSingleton.GetInstance: TSingleton;
begin
if FSingleton = nil then
begin
FSingleton := TSingleton.CreateInstance;
end;
Result := FSingleton;
end;
end.
0 件のコメント:
コメントを投稿