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