In a quite well-known pattern, you have a certain amount of classes, all inheriting from a common base and you have a factory that creates instances of these classes. Now let’s go further ahead and assume that the factory will have no knowledge of what classes will be available at run-time.

Each of this classes registers itself at run-time depending on a certain condition and then the factory will create instances depending on that registration.

This post is about how to do this in Delphi. Remember that this sample is very much abstracted and the real-world application is quite a bit more complex, but this sample should be enough to demonstrate the point.

Let’s say, we have these classes:

type
  TJob = class(TObject)
    public
      constructor Create;
  end;

  TJobA = class(TJob)
    public
      constructor Create;
  end;

  TJobB = class(TJob)
    public
      constructor Create;
  end;

  TJobAA = class(TJobA)
    public
      constructor Create;
  end;

Each of these constructors does something to initialize the instance and thus calls its parent using ‘inherited’.

Now, let’s further assume that we have a Job-Repository that stores a list of available jobs:

type
  TJobRepository = class(TObject)
    private
      FAvailableJobs: TList;
    public
      procedure registerJob(cls: TClass);
      function getJob(Index: Integer): TClass;
   end;

Now we can register our jobs

   rep = TJobRepository.Create;
   if condition then
     rep.RegisterJob(TJobAA);
   if condition2 then
     rep.RegisterJob(TJobB);

and so on. Now at runtime, depending on some condition, we will instantiate any of these registered jobs. This is how we’d do that:

  job = rep.getJob(0).Create; 

Sounds easy. But this doesn’t work.

job in this example will be of type TJobAA (good), but its constructor will not be called (bad). The solution is to

  1. Declare the constructor of TJob as being virtual.
  2. Create a Meta-Class for TJob, because the Constructor of TObject is NOT virtual, to when you dynamically instantiate an object from a TClass only the constructor of TObject will be called.
  3. Override the inherited virtual constructor.

So in code, it looks like this:

type
  TJobClass = class of TJob;
  TJob = class(TObject)
   public
    constructor Create; virtual;
  end;

  TJobA = class(TJob)
    public
      constructor Create; override;
    end;

TJobAA = class(TJobA)
    public
      constructor Create; override;
    end;

TJobRepository = class(TObject)
    private
      FAvailableJobs: TList;
    public
      procedure registerJob(cls: TClass);
      function getJob(Index: Integer): TJobClass;
   end

This way, Delphi knows that when you call

  job = rep.getJob(0).Create; 

that you are creating an instance of a TJobAA object which has a constructor that overrides the virtual Constructor of TJob by the virtue that the Class of TJobAA is a class of TJob.

Personally, I would have assumed that this just works without the need of declaring the Meta-Class and the trickery with the need to explicitly declare the constructor as virtual. But seeing that Delphi is a compiled static language, actually, I’m happy that this works at all.



blog comments powered by Disqus