#1
  1. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jan 2012
    Posts
    13
    Rep 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
  2. #2
  3. No Profile Picture
    Contributing User
    Devshed Regular (2000 - 2499 posts)

    Join Date
    Jan 2006
    Location
    Carlsbad, CA
    Posts
    2,057
    Rep Power
    383
    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.
  4. #3
  5. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    May 2012
    Posts
    151
    Rep Power
    3
    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.
  6. #4
  7. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jan 2012
    Posts
    13
    Rep Power
    0
    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!

    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.

    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.

    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

    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
  8. #5
  9. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jan 2012
    Posts
    13
    Rep Power
    0
    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
  10. #6
  11. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jan 2012
    Posts
    13
    Rep Power
    0
    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
  12. #7
  13. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    May 2012
    Posts
    151
    Rep Power
    3
    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.

    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;
  14. #8
  15. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jan 2012
    Posts
    13
    Rep 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

IMN logo majestic logo threadwatch logo seochat tools logo