Knowledge Base

Spring4D factory pattern

What options are there in the Spring4D framework that we don’t know about, but are very usable? Well, actually quite a lot. One of them is the Factory Pattern. It comes in very handy when you want to apply the open/closed principle. Your code is open for extension, but closed for modification.

A classic example is working with enumerations or numbers that indicate a certain state. This quickly creates all kinds of conditional structures that have to be adapted as soon as a new status or value is added. In the example below, certain code is executed depending on an invoice status. To split the code, the factory pattern was applied.

This is the definition of invoice and status:

type
  {$SCOPEDENUMS ON}
  TInvoiceStatus = (New, Sent, Overdue, Paid);
  {$SCOPEDENUMS OFF}

  TInvoice = class
  public
    function Status: TInvoiceStatus;
  end;
To use the factory pattern, we include the Spring.DesignPatterns unit.
Next, we define the factory. It consists of a key value and a function that returns the method you want to execute. That sounds complex, but it’s not so bad. In our case, the key value is the invoice status (TInvoiceStatus). The function we want to execute has invoice (TInvoice) as a parameter. The definition for the factory then looks like this:
type
  TProcessInvoiceProc = reference to procedure(const Invoice: TInvoice);

  TInvoiceProcessor = class
  private
    FFactory: TFactory<TInvoiceStatus, TProcessInvoiceProc >;
    procedure InitialiseFactory;
  end;

procedure TInvoiceProcessor .InitialiseFactory;
begin
  FFactory := TFactory<TInvoiceStatus, TProcessInvoiceProc >.Create;

  FFactory.RegisterFactoryMethod(TInvoiceStatus.New, ProcessInvoiceNew);
  FFactory.RegisterFactoryMethod(TInvoiceStatus.Sent, ProcessInvoiceSent);
  FFactory.RegisterFactoryMethod(TInvoiceStatus.Overdue, ProcessInvoiceOverdue);
  FFactory.RegisterFactoryMethod(TInvoiceStatus.Paid, ProcessInvoicePaid);
end;
The code above already shows how the factory is created and initialised. Associated with each status is a “Process” function. This function returns the definition of the TProcessInvoiceProc to execute the code for that status. You define that as follows:
function TInvoiceProcessor .ProcessInvoiceNew: TProcessInvoiceProc;
begin
  Result := procedure(const Invoice: TInvoice)
            begin
              ShowMessage('This is a new invoice');
            end
end;

Applying the factory is then easy and goes as visible in the code below:

procedure TInvoiceProcessor .ProcessInvoice(const Invoice: TInvoice);
begin
  if FFactory.IsRegistered(Invoice.Status) then
  begin
    var Process := FFactory.GetInstance(Invoice.Status;)
    Process(Invoice);
  end;
end;
This way, using the factory pattern, your code is easy to split and improve.

Written by Kees de Kraker
Directeur

Contact

Let us help you to realise your ambitions

GDK Software UK

(+44) 20 3355 4470

GDK Software USA

+1 (575) 733-5744