Compago

...free knowledge

 
  • Increase font size
  • Default font size
  • Decrease font size
Home Manuali Programmazione Esempio di classe TSingleton in delphi

Esempio di classe TSingleton in delphi

E-mail Stampa PDF

In questo articolo vorrei proporre un esempio di singleton implementato in Delphi. Il singleton rappresenta un tipo particolare di classe che garantisce che soltanto un'unica istanza della classe stessa possa essere creata all'interno di un programma.
Tutte le variabili di questo tipo faranno sempre riferimento ad un solo ed unico oggetto.

Per ottenere ciò useremo due variabili di tipo class var all'interno della classe e poi sovrascriveremo i due metodi virtual che si occupano della creazione e distruzione di un oggetto (NewIstance, FreeIstance).

type
TSingleton = class
class var
RefCount:integer;
Istanza:TObject;
strict private
class function NewInstance: TObject; override;
procedure FreeInstance; override;
public
...
//campi e metodi della classe
...
constructor Create;  reintroduce;
destructor Destroy;  reintroduce;
end;

Segue l'implementazione:

constructor TSingleton.Create;
begin
inherited;
if RefCount=1 then begin
...
//Inserire qui l'inizializzazione variabili
...
end;
end;

destructor TSingleton.Destroy;
begin
if RefCount=0 then begin
...
//Inserire qui la distruzione delle variabili
...
end;
inherited;
end;

procedure TSingleton.FreeInstance;
begin
Dec(RefCount);
if RefCount=0 then
inherited;
end;

class function TSingleton.NewInstance: TObject;
begin
if RefCount=0 then
Istanza:=inherited NewInstance;
Inc(RefCount);
Result:=Istanza;
end;

Va fatta particolare attenzione in caso di derivazione di un'altra classe da questa, dato che l'oggetto in presenza della classe base e di quella derivata sarà sempre lo stesso e così pure il contatore (refCount). Quindi occorre tenere conto della eventualità che siano presenti più tipologie di classi che fanno riferimento allo stesso oggetto, e che molto probabilmente hanno necessità di campi aggiuntivi, i quali magari dovranno essere inizializzati anche dopo la creazione dell'istanza base.
Quindi o si evita di ereditare questa classe e si modifica la nuova classe per renderla direttamente singleton oppure si dovrà modificare il constructor in modo che si accorga della necessità di ulteriori inizializzazioni.

Altra problematica è quella dell'uso di questa classe con i thread dato che si avrà accesso ad un unico oggetto. Sarà quindi necessari prendere le necessarie precauzioni per evitare conflitti tra i vari thread.

Vediamo un esempio di classe TSingleton e di una classe derivata TSingletonMod

interface
uses
SysUtils,Classes;

type
TSingleton = class
class var
RefCount:integer;
Istanza:TObject;
strict private
class function NewInstance: TObject; override;
procedure FreeInstance; override;
public
val:integer;
procedure ScriviDati;

constructor Create;  reintroduce;
destructor Destroy;  reintroduce;
end;

//Classe derivata
TSingletonMod = class(TSingleton)
class var
RefCount:integer; //Il contatore è dichiarato nuovamente
//per tenere traccia solo della classe derivata

public
lista:Tstringlist;
procedure ScriviDati;
constructor Create;  reintroduce;
destructor Destroy;  reintroduce;
end;

implementation

{ TSingleton }

constructor TSingleton.Create;
begin
inherited;
if RefCount=1 then begin
val:=1;
end;
end;

destructor TSingleton.Destroy;
begin
if RefCount=0 then begin
//niente da inizializzare in questo caso
end;
inherited;
end;

procedure TSingleton.FreeInstance;
begin
Dec(RefCount);
if RefCount=0 then
inherited;
end;

class function TSingleton.NewInstance: TObject;
begin
if RefCount=0 then begin
Istanza:=inherited NewInstance;
end;
Inc(RefCount);
Result:=Istanza;
end;

procedure TSingleton.ScriviDati;
begin
writeln(format('Ref counter TSingleton=%d val=%d',[RefCount,val]));
end;



{ TSingletonMod }

constructor TSingletonMod.Create;
begin
inherited;
inc(Refcount); //RefCount è il contatore della classe derivata
if (RefCount=1) then begin
lista:=TStringList.Create;
end;
end;

destructor TSingletonMod.Destroy;
begin
Dec(RefCount);
if (RefCount=0) then begin
lista.Free;
end;
inherited;
end;

procedure TSingletonMod.ScriviDati;
var s:string;
begin
writeln(format('Ref counter TSingletonMod=%d val=%d',[RefCount,val]));
for s in lista do
writeln(s);
end;

end.

Facciamo un test per le classi:

var
S1,S2:TSingleton;
M1,M2:TSingletonMod;
begin
S1:=TSingleton.Create;   //Creo la prima variabile singleton
S1.val:=5;
S1.ScriviDati;
S2:=TSingleton.Create;  //Incremento il contatore di singleton
S2.val:=7;
S1.ScriviDati;          //I dati sono sempre gli stessi per entrambi
S2.ScriviDati;

M1:=TSingletonMod.Create;  //Creo la prima variabile singleton modificato
M1.lista.Add('A');
M1.ScriviDati;
M2:=TSingletonMod.Create;  //Incremento il contatore singleton modificato
M2.lista.Add('B');
M1.ScriviDati;
S1.ScriviDati;
M1.Destroy;                //Decremento il contatore di singleton modificati
M1.ScriviDati;
M2.Destroy;                //Elimina le variabili in più SingletonMod -> Singleton

S1.ScriviDati;
S1.Destroy;                //Decremento il contatore di singleton
S2.Destroy;                //Distruggo il singleton

readln;
end.

Il risultato sarà

Ref counter TSingleton=1 val=5
Ref counter TSingleton=2 val=7
Ref counter TSingleton=2 val=7
Ref counter TSingletonMod=1 val=7
A
Ref counter TSingletonMod=2 val=7
A
B
Ref counter TSingleton=4 val=7
Ref counter TSingletonMod=1 val=7
A
B
Ref counter TSingleton=2 val=7

Ultimo aggiornamento ( Sabato 31 Luglio 2010 10:22 )  
Loading

Login




Chiudi