Alphasite

The programmers site

Estadísticas web

Estadisticas web

Publicidad

Languages

Inicio de sesión

Google AdSense

Encuesta

En línea

En este momento hay 0 usuarios y 13 invitados en línea.

Delphi Static Classes (Singleton Pattern)

When your day work involves programming in Delphi and you have programmed in a language such as C++ or C# which allow static class you sometimes miss them as they are an useful resource.

What's a static class

A static class is, simply put, a class that provides some methods, properties and even fields but which doesn't need an instance to be used, that is, you don't need to create an instance of the class in order to call its methods or access its properties.

Actually, in C# for example, this separation reach the method, field and property level, that is, it is possible to define some methods to be static while other ones are not, and so such methods may be called without having to instantiate the class. In C# that looks more or less like this:

public class MyStaticClass
{
  private int m_value;

  public MyStaticClass()
  {
    m_value = 0;
  }

  public int Value(){ return m_value; }

  public static int ModDivision(int numerator, int denominator)
  {
    int result = numerator;
    while (result < denominator)
    {
      result = result - denominator;
    }
    return result;
  }
} 

In this way the class has two methods one of which is static and one which is not, so that we can do call it as invocation:

MyStaticClass.ModDivision(30,4);

but we could not invoke the Value function without creating an instance of the class.

Static classes in Delphi

Unfortunately delphi implicitly does not provide any method for working with static classes, it provides a similar mechanism, which si named class procedure or class function that provides similar functionality but require the existence of a declaration of the appropriate type variable (see code in the appendix).

But we can simulate this functionality with a simple trick using two sections of a Delphi unit, the initialization section and the finalization section

The trick is to declare the class normally, then declare a private variable to hold the class, create a function that allows access to that class and finally instanstiate the class. Let's see example:

interface
type TMyStaticClass = class
  private
    m_value;
  public
    function Value : integer;
    function ModDivision(numerator,
                         denominator : integer) : integer;
end;

function MyStaticClass: TMyStaticClass;

implementation

var cInstance : TMyStaticClass;     // Variable which holds the class instance
    refCount : integer;             // Reference to the variable

// Access is implemented as a function so instance cannot be overwritten,
function MyStaticClass : TMyStaticClass;
begin
  if Assigned(cInstance) then
    result := cInstance

  else
    raise EInvalidPointer.Create('Invalid instance reference');
end;

{ Method implementation for the class here }

{ ************************************************************* }

initialization
begin
  // Make sure Application object is updated
  Application.ProcessMessages;
  // if the application is not closing (yep, that may happen)
  if not Application.Terminated then
  begin
    if (refCount = 0) then
    begin
      if cInstance = nil then
      begin
        // Create the instance if not already created
        cInstance := TMiClaseEstatica.Create;   
      end;
    end;
    Inc(refCount); // Increase reference count
  end;
end;

finalization
begin
  // Decrease reference count
  Dec(RefCount);
  // If reference count reach zero, free
  if RefCount = 0 then
  begin
    cInstance.Free;
    cInstance := nil;
  end;
end;

A small analysis[/H3]

In the previous code, the initialization and finalization sections are the key. When delphi starts loading the application it reads the .dpr and runs all the units specified in the uses clause and "process" these units.

The process is done by delphi (roughly) in two steps:

  1. Delphi recorre el uses de la unidad en cuestión y procesa cada unidad. Delphi visits the uses of the unit in question and processes each unit.
  2. Delphi runs the initialization section of the unit.

For practical purposes this means that any unit that uses our class will execute the initialization section before executing any code and therefore must already have created an instance of the class to which we can access.

Notes

  • RefCount. The variable is used to verify that only one instance is created and present at eany time and, most importantly, that it is destroyed only when nobody will need it anymore. It provides a behavior very similar to an interface.
  • Application.Terminated. It may happen that when the execution flow reachs our initialization section, one of the units that use our class also has an initialization section and, inside that section (or any of the hierarchical below) it decides to terminate the application (eg: because the operating system version isn't correct or not it failed to find any bookstore). Si esto ocurre es conveniente no inicializar la instancia puesto que realmente, si la aplicación esta cerrandose, no esta garantizado que las unidades de las que depende nuestra clase estén completamente inicializadas. If this happens the class should not be initialized as, if the application is closing, is not guaranteed that the units our class relys on will be fully initialized.
  • Lazy initialization pattern.The way we're creating our instance (singleton) during the initialization section it is always created regardless of its use throughout the application lifetime. Another pattern exists where the instance is only created when it is first used (hence the name lazy). Although this aproach has it benefits, it will have to deal with multithreading problems concerning the initialization of the instance wich could raise race conditions quite easily.

Apendix

interface

type MyNotSoStaticClass = class
  private
    m_val : integer := 43;
  public
    class function GetValue : integer;
end;

implementation

function MyNotSoStaticClass .GetValue : integer;
begin
  result := m_val;
end;

procedure TMainForm.Button1Click(Sender : TObject);
var MyClass : MyNotSoStaticClass;
begin
  ShowMessage(IntToStr(MiClase.GetValue));
end;

So as you see, with class functions it is necessary to make the declaration var MyClass: MyNotSoStaticClass to call the function which, although correct, is that a more cumbersome method than static classes.

Average: 4.5 (4 votes)

Responder

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
Image CAPTCHA
Copy the characters (respecting upper/lower case) from the image.