Jump to content
wuwuxin

Is there an elegant way in Delphi to prevent instantiating a class?

Recommended Posts

I want a class to be pure static, which doesn't allow instantiating (except through a static method for creating singleton).  Is that doable in Delphi?

Share this post


Link to post

If it's for a singleton, treat it like a Factory method with the constructor hidden. The static method would create it initially if it's NIL, and otherwise return the existing reference.

 

Also, make the constructor Create a strict private member of the class and only reference it through the static factory method in the same unit.

 

 

Edited by David Schwartz

Share this post


Link to post
4 hours ago, David Schwartz said:

If it's for a singleton, treat it like a Factory method with the constructor hidden. The static method would create it initially if it's NIL, and otherwise return the existing reference.

 

Also, make the constructor Create a strict private member of the class and only reference it through the static factory method in the same unit.

You cannot make such singleton in Delphi. Public TObject constructor will be always visible and you cannot hide it, 

 

The only way to disable access to the constructor is to define singleton API as interface and have public factory method that returns interface, and then put class implementation inside implementation section of the unit making it inaccessible to the outside world.

Share this post


Link to post

I wish the "class abstract" declaration would have prevented .Create instantiation.

  • Like 1

Share this post


Link to post
Guest
4 hours ago, wuwuxin said:

I want a class to be pure static, which doesn't allow instantiating (except through a static method for creating singleton).  Is that doable in Delphi?

If i do understand right and that you need a class that is no on the heap, then the answer is no, Delphi doesn't have static classes, though it is easy for the compiler to be made to do so.

On other hand the closest thing to static classes (on the stack or global as preallocated) is to use records, managed records is there to close some of this gap, but still these are records not classes.

 

I wish as a feature to have this declaration for classes

var
  C : TMyClass; static;        // C here is global and will not reside on heap
  D : TMyClass; static; auto;  // D is global and its default constructor will be handled as class constructor, same for its destructor, D is not on the stack

procedure Test;
var
  E : TMyClass; static;        // E reside on the stack
  F : TMyClass; static; auto;  // same as D but on the stack
  G : TMyclass; auto;          // G is on the heap but managed just like strings, means the compiler will manage its constructor and destructor
begin
  E := TMyclass.Create;  // manually managed hence need creating and free manually;
  F.DoSomething;         // the compiler will make sure to call the default Creat and Free in Test, F is on the stack !
  G.DoSomething;         // same as F but G reside on the heap
  

 

Share this post


Link to post
19 minutes ago, Dalija Prasnikar said:

You cannot make such singleton in Delphi. Public TObject constructor will be always visible and you cannot hide it, 

 

The only way to disable access to the constructor is to define singleton API as interface and have public factory method that returns interface, and then put class implementation inside implementation section of the unit making it inaccessible to the outside world.

I offered up a solution. It may not be ideal, but as you say, that's impossible.

 

So, yeah, we've gotta make do with what we've got. The language purists have managed to keep stuff like this from being properly implemented forever.


There are all kinds of holes and back-doors and things that can only be done with awkward work-arounds in Delphi, and I don't expect that will ever change.

 

Everywhere I've worked has all sorts of unwritten rules about what devs can and cannot do in order to deal with this stuff, and when I dig into the code there are also lots of violations of said rules, mostly by those who made the rules up in the first place. ("Do as I say, not as I do!")

  • Sad 1

Share this post


Link to post

There is another way to prevent instance creation - or at least misuse - having public constructor that raises exception.

 

  TSingleton = class
  private
    class var FInstance: TSingleton;
    constructor CreateSingleton;
    class destructor ClassDestroy;
  public
    constructor Create;
    class function Instance: TSingleton; static;
  end;


class destructor TSingleton.ClassDestroy;
begin
  FInstance.Free;
end;

constructor TSingleton.Create;
begin
  raise Exception.Create('Singleton instance cannot be directly constructed');
end;

constructor TSingleton.CreateSingleton;
begin
  // actual constructor
end;

class function TSingleton.Instance: TSingleton;
begin
  if not Assigned(FInstance) then
    FInstance := TSingleton.CreateSingleton;
  Result := FInstance;
end;

 

 

  • Like 7

Share this post


Link to post
12 minutes ago, DPStano said:

can ctor be private?


strict private
  constructor Create;
end;

 

Yes, it can. But the problem is not in hiding your constructor. 

 

Every class in Delphi descends from base TObject class, whether you explicitly state that in class declaration or not  

 

TSingleton = class

TSingleton = class(TObject)

 

And TObject has public constructor Create. That means its every descendant and consequently every other class in Delphi has public constructor Create. Since it is public it is accessible and you cannot prevent people using it.

 

So, even if you hide constructor in descendant class, someone can write TSingleton.Create and this will not call your hidden constructor, but TObject default constructor. 

 

The only way to hide that TObject constructor is to add public constructor with same name in your class, like in example I posted. But then you again have problem with having publicly accessible constructor and people can instantiate more than single object instance. If that public constructor raises exception, then at least code will fail at runtime showing that this constructor shouldn't be used.

Share this post


Link to post
On 9/6/2021 at 7:05 AM, Stefan Glienke said:

If you don't want instances, then don't make it a class - this ain't Java.

Thank you! Delphi has a method for grouping a collection of common procedures/functions together... it's called a Unit.

  • Like 2

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×