Introduction

The idea after the Abstract Factory pattern is the notion about our program (or the client of a class we are providing) working with several products (as those in a factory) which have some given characteristics (for example we have bottled products and products in a tetrabrik). Our program is going to use those products performing a couple of actions over them (like putting the bottles in one truck and the tetrabriks in another one) without caring about who is actually providing him with the products.

There are a series of factories as well that will produce those products we are going to handle, one fabric makes juice, another one milk and another one makes sauce but each of them have bottles and tetrabrik products which our program handle. In the end, the basic concept is that our program (or client) doesn’t care what’s inside the bottel or who has produced it as long as it is a bottle.

The software point of view

From the software point of view, the previous example relates to a situation where our program handles types of objects with some common characteristics and some of their own. That situation, generally, is solved by means of two characteristics of the object oriented programming languages, the abstract classes and interfaces.

The Problem

The Abstract Factory design pattern tries to solve the problem of the creation of object families (as for example in graphic user interfaces) which share a set of common characteristics in the objects composing those families.

Use

Use of this pattern is advised on situations where we have a concrete family of products and we foresee the possible use of different families in the future.

Structure

This diagram (from the wikipedia) describes perfectly the pattern.

This way our program performs certain operations on the common characteristics of the objects without caring about what other characteristic a given object may have. On the other hand there are several different producers which provide us with families of such objects. A classical example in a lot of frameworks which follows this pattern is the case of the graphical interface families. There are several families which provide their own buttons, text boxes, combo boxes and so on… all of them based on the basic types. The clients obtain a family and proceed to use the controls by using the abstract type which supports the implementation.

Example code

Let’s see an example (without the implementation section)

// Abstract class which describe a communications control
type TComControl = class
  // Initializes the control (but doesn’t connect)
  procedure Init;
  // Sends a keep alive message
  procedure SendKeepAlive;
  // Stablish the actual connection
  procedure Connect(remoteName : string);
  // Terminates the connection
  procedure Disconnect;
end;

// Abstract class which describes a communication
// Handler (allows to send and receive)
type TComHandler = class
  // Sends a text string
  procedure SendText(text : string);
  // Sends a stream
  procedure SendStream(buffer : TStream);
  // Receives a text string
  // returns -1 on timeout, 0 otherwise
  function ReadText(var text : string; timeout : integer);
  // Receives a stream
  // returns -1 on timeout, 0 otherwise
  function ReadStream(buffer : TStream; timeout : integer);
end;

type TComFactory = class
  procedure GetHandler : TComControl;
  procedure GetControl : TComHandler;
end;

Previous objects are abstract classes which do not provide with functionalities to perform communication between two points. Instead the Control object allows us to manage our connection while the Handler object allows us to send and receive information.

unit ComUDP;

// The actual real classes for UDP
// UDP Connection control
type TUDPControl = class(TComControl)
  procedure Init; override;
  procedure SendKeepAlive; override;
  procedure Connect(remoteName : string); override;
  procedure Disconnect; override;
end;

// UDP Handler
type TUPPHandler = class(TComHandler)
  procedure SendText(text : string);
  procedure SentStream(buffer : TStream);
  function ReadText(var text : string; timeout : integer);
  function ReadStream(buffer : TStream; timeout : integer);
end;

// The UDP communication factory
type TUDPFactory = class(TComFactory)
  procedure GetHandler : TComControl;
  procedure GetControl : TComHandler;
end;
unit ComTCP;

// The actual real classes for TCP
// TCP Control
type TTCPControl= class
  procedure Init; override;
  procedure SendKeepAlive; override;
  procedure Connect(remoteName : string); override;
  procedure Disconnect; override;
end;

// TCP Handler
type TTCPHandler = class
  procedure SendText(text : string);
  procedure SentStream(buffer : TStream);
  function ReadText(var text : string; timeout : integer);
  function ReadStream(buffer : TStream; timeout : integer);
end;

// The TCP Factory
type TTCPFactory = class(TComFactory)
  procedure GetHandler : TComControl;
  procedure GetControl : TComHandler;
end;

Finally, our main program will look something like this:

function GetFactory : TComFactory;
begin
  if Config.ComType = 'TCP' then
    result := TTCPFactory.Create
  else
    result := TUDPFactory.Create;
end;

program Comunicaciones;
var
  ComFactory : TComFactory;
  message : string;
begin
  ComFactory := GetFactory;
  ComFactory.GetControl.Init;
  ComFactory.GetControl.Connect;
  if (ComFactory.GetHandler.SendText('Hello World', 2000) = -1) then
     ShowMessage('Error sending the message');
  if (ComFactory.GetHandler.Read(message,2000) = -1) then
     ShowMessage('Error receiving the mesage')
  else
     ShowMessage('Message received: ' + message);
  ComFactory.GetControl.Disconnect;
end;

Now our program gets just one factory and uses it without worrying about the real implementation of the class but focusing only in the basic (abstract) functionality of those instances.

Real examples

Communications family

A fairly common case is similar to the one shown in the previous code example, that is, a communication family for different transmission methods so that our application can use TCP, UDP or any other protocol for data transmission (for example no standard ones over uncommon hardware) without the business logic being affected by it.

Graphic interfaces

Another relatively common case of this pattern happens in the creation of graphic interface families in which all the elements of the interfaces are constant (for example labels, buttons, text boxes…) but the actual drawing of those elements can be delegated upon those families (for example QT, GTK, Windows Forms, etc) so that, given a selected factory we get a set of controls or another.

Leave a Reply