Delphi Programming
 
Forums: » Register « |  User CP |  Games |  Calendar |  Members |  FAQs |  Sitemap |  Support | 
User Name:
Password:
Remember me

The Shed is going Social! Join us on FaceBook and Twitter and chime in on the conversation.

Go Back   Dev Shed ForumsProgramming Languages - MoreDelphi Programming

Reply
Add This Thread To:
  Del.icio.us   Digg   Google   Spurl   Blink   Furl   Simpy   Y! MyWeb 
Thread Tools Search this Thread Rate Thread Display Modes
 
Unread Dev Shed Forums Sponsor:
  #1  
Old June 19th, 2012, 02:34 PM
Marlon B Marlon B is offline
Registered User
Dev Shed Newbie (0 - 499 posts)
 
Join Date: Jan 2012
Posts: 11 Marlon B User rank is Just a Lowly Private (1 - 20 Reputation Level) 
Time spent in forums: 2 h 43 m 13 sec
Reputation Power: 0
Question Easiest way to create a singleton?

Hi everybody,

Back in the days when I was programming in Java I've used some singletons here and there. Basically you hid the constructor, made sure the singleton instance was created in a thread-safe manor, and voila, you had a singleton ready to be used.

Browsing through a lot of Google results, I've noticed that almost each example in Delphi is much more complicated, including up to and including a new factory class to create the singleton!

I've had a quick go myself, and this is what I've come up with: basically, I need two singletons - one to handle reading and writing settings, the other to handle logging throughout the application.

Example code for the Settings singleton:

Code:
unit Settings;

interface

uses
  SysUtils, Windows;

type
  TSettings = class
    private
      constructor Create;
    public
      class function NewInstance: TObject; override;
  end;

  function AppSettings : TSettings;

implementation

var
  pSettings : TSettings;

constructor TSettings.Create;
begin
  //
end;

class function TSettings.NewInstance : TObject;
begin
  result := pSettings;
end;

function AppSettings : TSettings;
begin
  result := pSettings;
end;

initialization
  pSettings := TSettings.Create;

finalization
  if Assigned(pSettings) then
  begin
    FreeAndNil(pSettings);
  end;

end.
Basically I'm handling the singleton as a glorified global variable (via AppSetting).

I'm probably missing something in this solution, but I can't seem to find out what. The constructor is hidden, NewInstance cannot return another instance, ... The only thing I can see that's a possible problem is the initialization part, which should be handled in a critical section (or shouldn't it?).

If there's anybody out there who can explain to me whether this is a good idea or not (and if not, why), then I'd be very very grateful!

Thanks!

Marlon

Reply With Quote
  #2  
Old June 19th, 2012, 04:43 PM
clivew clivew is offline
Contributing User
Dev Shed Regular (2000 - 2499 posts)
 
Join Date: Jan 2006
Location: Carlsbad, CA
Posts: 2,045 clivew User rank is Major (30000 - 40000 Reputation Level)clivew User rank is Major (30000 - 40000 Reputation Level)clivew User rank is Major (30000 - 40000 Reputation Level)clivew User rank is Major (30000 - 40000 Reputation Level)clivew User rank is Major (30000 - 40000 Reputation Level)clivew User rank is Major (30000 - 40000 Reputation Level)clivew User rank is Major (30000 - 40000 Reputation Level)clivew User rank is Major (30000 - 40000 Reputation Level)clivew User rank is Major (30000 - 40000 Reputation Level)clivew User rank is Major (30000 - 40000 Reputation Level) 
Time spent in forums: 1 Week 6 Days 2 h 37 m
Reputation Power: 382
Depends upon which version of Delphi you are using.

I think it was Delphi 2010 that introduced class constructors.
Class constructors and class variables are the way to go in recent Delphi versions.

Reply With Quote
  #3  
Old June 19th, 2012, 11:41 PM
Luthfi Luthfi is offline
Contributing User
Dev Shed Newbie (0 - 499 posts)
 
Join Date: May 2012
Posts: 140 Luthfi User rank is Just a Lowly Private (1 - 20 Reputation Level) 
Time spent in forums: 1 Day 4 h 53 m 11 sec
Reputation Power: 2
Quote:
Originally Posted by Marlon B
Hi everybody,

Back in the days when I was programming in Java I've used some singletons here and there. Basically you hid the constructor, made sure the singleton instance was created in a thread-safe manor, and voila, you had a singleton ready to be used.

Browsing through a lot of Google results, I've noticed that almost each example in Delphi is much more complicated, including up to and including a new factory class to create the singleton!

I've had a quick go myself, and this is what I've come up with: basically, I need two singletons - one to handle reading and writing settings, the other to handle logging throughout the application.

Example code for the Settings singleton:

Code:
unit Settings;

interface

uses
  SysUtils, Windows;

type
  TSettings = class
    private
      constructor Create;
    public
      class function NewInstance: TObject; override;
  end;

  function AppSettings : TSettings;

implementation

var
  pSettings : TSettings;

constructor TSettings.Create;
begin
  //
end;

class function TSettings.NewInstance : TObject;
begin
  result := pSettings;
end;

function AppSettings : TSettings;
begin
  result := pSettings;
end;

initialization
  pSettings := TSettings.Create;

finalization
  if Assigned(pSettings) then
  begin
    FreeAndNil(pSettings);
  end;

end.
Basically I'm handling the singleton as a glorified global variable (via AppSetting).

I'm probably missing something in this solution, but I can't seem to find out what. The constructor is hidden, NewInstance cannot return another instance, ... The only thing I can see that's a possible problem is the initialization part, which should be handled in a critical section (or shouldn't it?).

If there's anybody out there who can explain to me whether this is a good idea or not (and if not, why), then I'd be very very grateful!

Thanks!

Marlon


I did wrote about singleton in Delphi and discussed the pros and cons each known methods. Check it out here: http://forum.codecall.net/topic/66865-design-pattern-in-delphi-singleton/

On your code:
The main problem is that you did not handle the destructor. With your approach, you can still free TSettings by calling AppSettings.Free;. And when it did happen your finalization will call Free on invalid object. EAccessViolation will be raised. Even before the finalization, a subsequent calls to AppSettings.Free will also raise EAccessViolation.

The second problem is that you had broken the "standard" to create objects (i.e. by calling Create) since now it's NewInstance. And since NewInstance actually existing methods (introduced in TObject, so all objects have this method), later it will confuse you since code completion could not help you.

Last, but not least (although this actually is your real first problem with TSettings), is that you did not call inherited NewInstance anywhere in your TSettings.NewInstance. You just return pAppSettings. Prior running codes inside constructors, Delphi compiler always calls NewInstance to allocate memory for the object for the contructors to work with. If NewInstance returns existing object, then the contructors will work with existing object. Since your TSettings.NewInstance always returns pSettings, and pSettings will always be initialized to nil, making codes in your constructor (TSettings.Create) will always works with invalid object (i.e. nil). Basically in the current state, pSettings will always be nil, regardless how many time you call TSettings.Create or TSettings.NewInstance.

Check out my tutorial in the link I gave you above. There you will find working samples of 3 approaches for singleton implementation in Delphi.

Reply With Quote
  #4  
Old June 20th, 2012, 08:42 AM
Marlon B Marlon B is offline
Registered User
Dev Shed Newbie (0 - 499 posts)
 
Join Date: Jan 2012
Posts: 11 Marlon B User rank is Just a Lowly Private (1 - 20 Reputation Level) 
Time spent in forums: 2 h 43 m 13 sec
Reputation Power: 0
Quote:
Originally Posted by Luthfi
I did wrote about singleton in Delphi and discussed the pros and cons each known methods. Check it out here: http://forum.codecall.net/topic/66865-design-pattern-in-delphi-singleton/

Thank you for the link, your post is very clear and helpful!

Quote:
Originally Posted by Luthfi
On your code:
The main problem is that you did not handle the destructor. With your approach, you can still free TSettings by calling AppSettings.Free;. And when it did happen your finalization will call Free on invalid object. EAccessViolation will be raised. Even before the finalization, a subsequent calls to AppSettings.Free will also raise EAccessViolation.

Indeed - did not spot that one.

Quote:
Originally Posted by Luthfi
The second problem is that you had broken the "standard" to create objects (i.e. by calling Create) since now it's NewInstance. And since NewInstance actually existing methods (introduced in TObject, so all objects have this method), later it will confuse you since code completion could not help you.

That was actually my feeble attempt to a) hide the constructor, and b) shield people from using NewInstance to create a new object.

Quote:
Originally Posted by Luthfi
Last, but not least (although this actually is your real first problem with TSettings), is that you did not call inherited NewInstance anywhere in your TSettings.NewInstance. You just return pAppSettings. Prior running codes inside constructors, Delphi compiler always calls NewInstance to allocate memory for the object for the contructors to work with. If NewInstance returns existing object, then the contructors will work with existing object. Since your TSettings.NewInstance always returns pSettings, and pSettings will always be initialized to nil, making codes in your constructor (TSettings.Create) will always works with invalid object (i.e. nil). Basically in the current state, pSettings will always be nil, regardless how many time you call TSettings.Create or TSettings.NewInstance.

When I first read this part of your post, my initial reaction was "that can't be, since the code works - the object cannot be nil!". But I've re-read it a few times, looked over the code, and now I understand where I went wrong. The code worked until I added my NewInstance procedure (as mentioned above). Thanks for pointing this out - this would've taken me some time to find out

Quote:
Originally Posted by Luthfi
Check out my tutorial in the link I gave you above. There you will find working samples of 3 approaches for singleton implementation in Delphi.

Again, thanks for the tutorial!

Marlon

Reply With Quote
  #5  
Old June 20th, 2012, 08:43 AM
Marlon B Marlon B is offline
Registered User
Dev Shed Newbie (0 - 499 posts)
 
Join Date: Jan 2012
Posts: 11 Marlon B User rank is Just a Lowly Private (1 - 20 Reputation Level) 
Time spent in forums: 2 h 43 m 13 sec
Reputation Power: 0
Quote:
Originally Posted by clivew
Depends upon which version of Delphi you are using.

I think it was Delphi 2010 that introduced class constructors.
Class constructors and class variables are the way to go in recent Delphi versions.


Thanks for mentioning this - I mostly use Delphi 2010, but at work we're still stuck with Delphi 7... That would've been a nasty surprise

Marlon

Reply With Quote
  #6  
Old June 21st, 2012, 07:58 AM
Marlon B Marlon B is offline
Registered User
Dev Shed Newbie (0 - 499 posts)
 
Join Date: Jan 2012
Posts: 11 Marlon B User rank is Just a Lowly Private (1 - 20 Reputation Level) 
Time spent in forums: 2 h 43 m 13 sec
Reputation Power: 0
Quote:
Originally Posted by Luthfi
I did wrote about singleton in Delphi and discussed the pros and cons each known methods. Check it out here: http://forum.codecall.net/topic/66865-design-pattern-in-delphi-singleton/
Sorry to bother you again, Luthfi, but there's still something that I don't quite understand. I've downloaded your example from the URL above and tried it, fiddled around a bit with my own code, but I'm still stuck with a small problem.

Since I'm no longer allowed to add a public constructor (which would defeat the purpose of the singleton), I need to do the initialization somewhere else. I figured I'd alter NewInstance and FreeInstance as follows:
Code:
class function TSettings.NewInstance : TObject;
begin
  if (pSettings = nil) then
  begin
    pSettings := inherited NewInstance;
    TSettings(pSettings).CreateInstance;
  end;
  result := pSettings;
end;

procedure TSettings.FreeInstance;
begin
  if (pFinalized) then
  begin
    TSettings(pSettings).DestroyInstance;
    inherited FreeInstance;
  end;
end;
I've added two procedures, CreateInstance and DestroyInstance, both private, to handle some stuff that would normally be handled in Create and Destroy.

But since pSettings is of type TObject rather than TSettings, I need to typecast them. Changing pSettings to TSettings is not an option, because then I break the NewInstance procedure.

Somehow this doesn't feel quite 'right' - is there a better way to handle the code that would normally be found in Create or Destroy?

Thanks in advance!

Marlon

Reply With Quote
  #7  
Old June 21st, 2012, 08:36 AM
Luthfi Luthfi is offline
Contributing User
Dev Shed Newbie (0 - 499 posts)
 
Join Date: May 2012
Posts: 140 Luthfi User rank is Just a Lowly Private (1 - 20 Reputation Level) 
Time spent in forums: 1 Day 4 h 53 m 11 sec
Reputation Power: 2
Quote:
Originally Posted by Marlon B
But since pSettings is of type TObject rather than TSettings, I need to typecast them. Changing pSettings to TSettings is not an option, because then I break the NewInstance procedure.

Somehow this doesn't feel quite 'right' - is there a better way to handle the code that would normally be found in Create or Destroy?


Actually you still can use Create to initialize the singleton, as long codes inside it respect a flag that you had to set once in the constructor. Something like this:

Code:
TSettings=class
private
  FInitialized: Boolean;
public
  constructor Create; override;
end;

implementation
...
..

constructor TSettings.Create;
begin
  if FInitialized then Exit;
  ... // do your initialization here
  ...

  FInitialized := True;
end;


Same thing can be applied in the destructor. Codes inside it must check another flag, in your case you can just use pFinalized.

Quote:
But since pSettings is of type TObject rather than TSettings, I need to typecast them. Changing pSettings to TSettings is not an option, because then I break the NewInstance procedure.


No, you can use TSettings for pSettings' type. No problem about that. You can use either one of the following NewInstance codes in this case.

Code:
...
...
var
  pSettings: TSettings;
...

function TSettings.NewInstance: TObject;
begin
  if pSettings=nil then
    pSettings := TSettings(inherited NewInstance);

  Result := pSettings;
end;


or

Code:
...
...
var
  pSettings: TSettings;
...

function TSettings.NewInstance: TObject;
begin
  if pSettings=nil then
  begin
    Result := inherited NewInstance;
    pSettings := TSettings(Result);
  end
  else
    Result := pSettings;
end;

Reply With Quote
  #8  
Old June 21st, 2012, 11:33 AM
Marlon B Marlon B is offline
Registered User
Dev Shed Newbie (0 - 499 posts)
 
Join Date: Jan 2012
Posts: 11 Marlon B User rank is Just a Lowly Private (1 - 20 Reputation Level) 
Time spent in forums: 2 h 43 m 13 sec
Reputation Power: 0
Indeed, I failed to spot the flag that was needed inside the constructor (and destructor) - similar to NewInstance and FreeInstance. Of course, I ended up with the same object, but with different values coming through the constructor

Thanks again Luthfi for your help!

Marlon

Reply With Quote
Reply

Viewing: Dev Shed ForumsProgramming Languages - MoreDelphi Programming > Easiest way to create a singleton?

Developer Shed Advertisers and Affiliates



Thread Tools  Search this Thread 
Search this Thread:

Advanced Search
Display Modes  Rate This Thread 
Rate This Thread:


Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off
View Your Warnings | New Posts | Latest News | Latest Threads | Shoutbox
Forum Jump

Forums: » Register « |  User CP |  Games |  Calendar |  Members |  FAQs |  Sitemap |  Support | 
  
 


Powered by: vBulletin Version 3.0.5
Copyright ©2000 - 2013, Jelsoft Enterprises Ltd.

© 2003-2013 by Developer Shed. All rights reserved. DS Cluster - Follow our Sitemap