Visit our Sponsor   Visit our Sponsor
delphi3000.com - the free delphi knowledge platform
delphi3000.com - the free delphi knowledge platform
Have a look at your member-status

connecting people's knowledge


  - Recent ArticlesRSS feed for Recent Articles on delphi3000.com
  - List of All Articles
  - Top Viewed Articles
  - Articles (+Attachem.)
  - Articles Of Interest
  - Categories
  - Top Uploader
  - Search
  - Index

  - My Home
  - Submit an Article
  - My Articles
  - My Personal Data
  - My Bookmarks
  - Activities
  - Login/Logout

  - Sign Up
  - Why Sign Up
  - Newsletter

  - Press
  - Advertise

  - Contact
  - Feedback





Community
Borland
ClubeDelphi
Dr. Bob
UK-BUG
Delphi Meetings
Planeta Delphi







Startblatt.de






Share this article with friendsShare this article with friends
Rate this articleRate this article - to keep the quality of delphi3000.com !
Comment this article or read through previous comments (2)


Safety Design with a Static InstanceGo to Max Kleiner's websiteFormat this article printer-friendly!Bookmark function is only available for registered users!
How to build a real Singleton ?
Product:
Delphi all versions
Category:
Object Pascal
Skill Level:
Scoring:
Last Update:
09/22/2002
Search Keys:
delphi delphi3000 article borland vcl code-snippet singleton class-reference pattern
Times Scored:
4
Visits:
2826
Uploader: Max Kleiner
Company: kleiner kommunikation
Reference: N/A
 
Question/Problem/Abstract:
The Singelton Pattern is widely used, on the other side OP lacks of statics, means one instance for all classes. No problem with the following design which acts like a time-server.
Answer:



Sometimes operations are performed on a class itself, rather than on instances of a class (that is, objects). This happens, for example, when you call a constructor method using a class reference.

TTimeKeeper = class;
TTimeKeeperClass = class of TTimeKeeper;

You can always refer to a specific class using its name, but at times it is necessary to declare variables or parameters that take classes as values, and in these situations you need class-reference types.
In our case we need a class-method and a global function too to get the one and only instance:

  class function Instance: TTimeKeeper;

  function TimeKeeper: TTimeKeeper;  //global function

When this function is called, a safety instance is returned:

function TimeKeeper: TTimeKeeper;
begin
  Result := TTimeKeeper.Instance;
end;

A class method is a method (other than a constructor) that operates on classes instead of objects. The definition of a class method must begin with the reserved word class. A class method can be called through a class reference or an object reference.
So the client calls the class method first:

procedure TMainDlg.NewBtnClick(Sender: TObject);
var myTimer: TTimeKeeper;
begin
  myTimer:=TimeKeeper;
  StatusBar.Panels[0].Text:=timeToStr(myTimer.now);
end;

And the class method returns the protected and local instance:

class function TTimeKeeper.Instance: TTimeKeeper;
// Single Instance function - create when first needed
begin
  Assert(Assigned(TimeKeeperClass));
  if not Assigned(TimeKeeperInstance) then
    TimeKeeperInstance := TimeKeeperClass.SingletonCreate;
  Result := TimeKeeperInstance;
end;

******************************************************************************************
unit SafetyTimeKeeper;

interface

uses
  SysUtils;

type
  ESingleton = class(Exception);

  TInvalidateDestroy = class(TObject)
  protected
    class procedure SingletonError;
  public
    destructor Destroy; override;
  end;

  TTimeKeeper = class;
  TTimeKeeperClass = class of TTimeKeeper;
  TTimeKeeper = class(TInvalidateDestroy)
  private
    class procedure Shutdown;
    function GetTime: TDateTime;
    function GetDate: TDateTime;
    function GetNow: TDateTime;
  protected
    // Allow descendents to set a new class for the instance:
    class procedure SetTimeKeeperClass(aTimeKeeperClass: TTimeKeeperClass);
    // Actual constructor and destructor that will be used:
    constructor SingletonCreate; virtual;
    destructor SingletonDestroy; virtual;
  public
    // Not for use - for obstruction only:
    class procedure Create;
    class procedure Free(Dummy: integer);
{$IFNDEF VER120} {$WARNINGS OFF} {$ENDIF}
    // This generates warning in D3. D4 has reintroduce keyword to solve this
    class procedure Destroy(Dummy: integer); {$IFDEF VER120} reintroduce; {$ENDIF}
    // Simple interface:
    class function Instance: TTimeKeeper;
    property Time: TDateTime read GetTime;
    property Date: TDateTime read GetDate;
    property Now: TDateTime read GetNow;
  end;
{$IFNDEF VER120} {$WARNINGS ON} {$ENDIF}

function TimeKeeper: TTimeKeeper;

implementation

class procedure TInvalidateDestroy.SingletonError;
// Raise an exception in case of illegal use
begin
  raise ESingleton.CreateFmt('Illegal use of %s singleton instance!', [ClassName]);
end;

destructor TInvalidateDestroy.Destroy;
// Protected against use of default destructor
begin
  SingletonError;
end;

{ TTimeKeeper }
var
  TimeKeeperInstance: TTimeKeeper = nil;
  TimeKeeperClass: TTimeKeeperClass = TTimeKeeper;

class procedure TTimeKeeper.SetTimeKeeperClass(aTimeKeeperClass: TTimeKeeperClass);
// Allow change of instance class
begin
  Assert(Assigned(aTimeKeeperClass));
  if Assigned(TimeKeeperInstance) then SingletonError;
  TimeKeeperClass := aTimeKeeperClass;
end;

class function TTimeKeeper.Instance: TTimeKeeper;
// Single Instance function - create when first needed
begin
  Assert(Assigned(TimeKeeperClass));
  if not Assigned(TimeKeeperInstance) then
    TimeKeeperInstance := TimeKeeperClass.SingletonCreate;
  Result := TimeKeeperInstance;
end;

class procedure TTimeKeeper.Shutdown;
// Time to close down the show
begin
  if Assigned(TimeKeeperInstance) then begin
    TimeKeeperInstance.SingletonDestroy;
    TimeKeeperInstance := nil;
  end;
end;

constructor TTimeKeeper.SingletonCreate;
// Protected constructor
begin
  inherited Create;
end;

destructor TTimeKeeper.SingletonDestroy;
// Protected destructor
begin
  // We cannot call inherited Destroy; here!
  // It would raise an ESingleton exception
end;

// Protected against use of default constructor
class procedure TTimeKeeper.Create;
begin
  SingletonError;
end;
// Protected against use of Free
class procedure TTimeKeeper.Free(Dummy: integer);
begin
  SingletonError;
end;

class procedure TTimeKeeper.Destroy(Dummy: integer);
begin
  SingletonError;
end;

// Property access methods
function TTimeKeeper.GetDate: TDateTime;
begin
  Result := SysUtils.Date;
end;

function TTimeKeeper.GetNow: TDateTime;
begin
  Result := SysUtils.Now;
end;

function TTimeKeeper.GetTime: TDateTime;
begin
  Result := SysUtils.Time;
end;

// Simplified functional interface

function TimeKeeper: TTimeKeeper;
begin
  Result := TTimeKeeper.Instance;
end;

initialization
finalization
// Destroy when application closes
  TTimeKeeper.Shutdown;
end.
  





Please rate this article!
Skill level:
BeginnerExpert

Useful:
No!Very!

Overall rating:
PoorExcellent



Comments to this article
Write a new comment
Another way to hide Create
    Yoav Abrahami (Oct 1 2002 11:55AM)

First, let me say that your way to make a singleton object is a good one, and it is good practive to use singletons.

I can see from your implementation that you had a problem to hide the objects create and destroy methods, and there for you raise exceptions for improper use. I can show you another way to solve this problem - take a look at http://www.delphi3000.com/articles/article_1736.asp.
If this implementation, I use the NewInstance and FreeInstance methods of TObject to change the way create and free works to be as I want them to operate. I then define a GetInstance Constructor that gets you the instance - the single instance created by NewInstance.

Yoav.


Respond

RE: Another way to hide Create
Max Kleiner (Oct 2 2002 9:57AM)

I appreciate your solution but manipulate the VCL should be the last render of resort so we do have a lots of code reviews to ensure the right use of an singleton.
Respond














 
Sign up to consume product discounts for Bronze memberships !

read more


  Visit our Sponsor

 

  Community Ad of
M. Maes
 
   














 







     
  Copyright © 2000 - 2007 delphi3000.com - All rights reserved. Terms of use. || Privacy
delphi3000.com is a service by bluestep.com IT-Services GmbH (Vienna)