Contents
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.