Delphi單例模式(singleton)

來源:互聯網
上載者:User

 

所謂單例就是系統中只能存在某個類的一個執行個體,在現實中只能存在一個執行個體的對象是很常見的,比如系統設定物件只能有一個,

比如一個用戶端同伺服器的TCP/IP串連經常只允許有一個串連等等。下面是一個單例模式的UML圖:

單例模式的實現

 

那麼如何保證在系統中每時每刻只有一個類的執行個體存在呢,這可以通過靜態變數來實現,在調用GetInstance時判斷靜態變數是否為nil,
如果為nil表示系統中沒有類的執行個體,則構造對象,同時將類執行個體賦值給靜態變數,如果不為nil,則直接返回靜態變數對應的類的執行個體。
Java或者C++中,類中的變數可以修飾為Static表示該變數不依賴於類的執行個體而單獨存在,在Delphi中沒有類似的關鍵字,所以只能是
定義一個單元的私人變數來實現對類執行個體的引用計數。

 

同時,另外在Java中為了防止使用者使用類的建構函式來建立多個類的執行個體,需要將建構函式的存取屬性改為private,但是在Delphi中,
編譯器對建構函式的保護層級進行了特殊的處理,即便將Contructor方法設定為private存取許可權,編譯器仍然會將Contructor的保護級
別修正為public,因此將調整建構函式的保護層級防止多例的產生在Delphi中是行不通的。幸好TObject基類中定義了NewInstance方法,
這個方式是一個類方法,通過編譯器魔法系統在每次構造對象時都會調用這個類方法,那麼通過重載這個靜態方法,就可以實現對建構函式的控制了。

 

下面就是一個單例的配置類示意代碼:

 

 
TSingleConfig=class(TObject)

  private

    FConfigPath:
string;

    procedure
SetConfigPath(const Value: string);

  public

    class function
GetInstance():TSingleConfig;

   
//
系統配置路徑

    property
ConfigPath:string read FConfigPath write SetConfigPath;

   
//...
省略

    class function
NewInstance: TObject; override;   

    procedure
FreeInstance;override;

  end;

 

implementation

 

var

 
GlobalConfig:TSingleConfig=nil;//單元內私人的靜態設定物件變數

 

{ TSingleConfig
}

 

procedure
TSingleConfig.FreeInstance;

begin

  inherited;

  GlobalConfig:=nil;

end;

 

class function
TSingleConfig.GetInstance: TSingleConfig;

begin

  if not
Assigned(GlobalConfig) then

   
GlobalConfig:=TsingleConfig.Create();

 
Result:=GlobalConfig;

end;

 

class function
TSingleConfig.NewInstance: TObject;

begin

  if not
Assigned(GlobalConfig) then

   
GlobalConfig:=TSingleConfig(inherited NewInstance);

 
Result:=GlobalConfig;

end;

 

procedure
TSingleConfig.SetConfigPath(const Value: string);

begin

  FConfigPath :=
Value;

end;

 

在靜態類方法GetInstance中,通過對靜態變數GlobalConfig對應的對象進行判斷來首先判斷GlobalConfig變數是否為nil
,如果為
nil,則表明系統中還沒有初始化對象的執行個體,這時調用私人的建構函式來初始化對象,並將其賦值給GlobalConfig
變數,如果不為nil,則返回已經建立的GlobalConfig對象。通過GlobalConfig的靜態變數,就可以保證對象的執行個體始終只有
一個(注意的是:
GlobalConfig對象需要聲明在Implementation部分,而不要聲明在單元Interface部分,這樣變數對單元外
的使用者是不可見的,這樣可以保護變數不會被使用者誤修改)。

 

此外,使用者可能會在使用完對象後,將其釋放,在Delphi中,一個對象被釋放後,它的執行個體對應的變數並不會自動設定為nil
,如果之後使用者再次調用
GetInstance獲得全域對象時,雖然對象已經被銷毀了,但是Assigned(GlobalConfig)仍然返回為真,
那麼
GetInstance就返回一個錯誤的指標,導致AV錯誤。為了避免這種情況,可以重載FreeInstance方法,該方法在對象被釋
放時總是會被調用的,在
FreeInstance方法中釋放對象後將GlobalConfig重新設定為nil就可以了。

 

VCL中的單例

 

VCL中也有很多的單例,比如剪貼簿類TClipboard類,在Clipbrd.pas單元中提供了類似於上面的執行個體控制技術,不過它是
通過函數
Clipboard來返回剪貼簿的唯一執行個體的,

function Clipboard:
TClipboard;

begin
  if FClipboard = nil then
    FClipboard := TClipboard.Create;
  Result := FClipboard;
end;

 

同樣的,它也在類的解構函式中將靜態變數設定為nil

 

destructor TClipboard.Destroy;
begin
  if (FClipboard = Self) then
    FClipboard := nil;
  inherited Destroy;
end;

 

不過同前面的重載NewInstance的方法相比,VCL中方法缺陷就是不能防止使用者多次建立使用者的執行個體,下面代碼分別
調用
TClipboard類和TSingleConfig類的Create方法兩次,然後比較兩次調用後獲得的執行個體的記憶體位址,判斷類是
被建立了幾次
:

 

procedure TForm1.btn1Click(Sender: TObject);
var
  P1:Pointer;
  P2:Pointer;
begin
  P1:=TClipboard.Create();
  P2:=Clipboard;
  ShowMessage(IntToStr(Integer(p1)));
  ShowMessage(IntToStr(Integer(p2)));
end;
 
procedure TForm1.btn2Click(Sender: TObject);
var
  P1:Pointer;
  P2:Pointer;
begin
  P1:=TSingleConfig.Create();
  P2:=TSingleConfig.Create();
  ShowMessage(IntToStr(Integer(p1)));
  ShowMessage(IntToStr(Integer(p2)));
end;
 
從運行結果可以知道TClipboard類的建構函式兩次調用後返回的地址不同,而TSingleConfig返回的地址則相同。
可以看出重載
NewInstance的方法更加嚴謹,不及出錯。

轉自:http://delphi.sharpplus.com/Delphi/singleton.htm

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.