Access:

» How I Learned To Love The Factory Pattern

Related categories:

Tom Jones
Viewed: 1714 | Article date: 2006-05-13 17:36:48

We create objects and we use objects. We sometimes even free objects! But in the end, that´s all we do with our object orientated programing language. Although the distinction between freeing and using is quite easy, the line between the creation of objects and their use is blurred. "Wait", you might say, "what about the constructor that comes with every object? Isn't that the place for the creation code and everything else is just using that object?"

We create objects and we use objects. We sometimes even free objects! But in the end, that's all we do with our object orientated programing language. Although the distinction between freeing and using is quite easy, the line between the creation of objects and their use is blurred. "Wait", you might say, "what about the constructor that comes with every object? Isn't that the place for the creational code and everything else is just using that object?"

About the author

Tom Jones is working as a software architect at ISOware GmbH, Munich. Contact him at tom.jones@isoware.com

You're right, but just in theory. If you're honest, you've written code like this a thousand times:

o := TSomeObject.Create ("Hello");

o.SomeProp := "OK";

The code snippet can be seen as a complete initialization of an object, or as the creation and use of an object. This may sound fussy, but the point is that it's good to keep this difference in mind - not in a small 1000-lines project, but in a big one where good architecture is vital for success.

The Factory Pattern can't make this design decision for you, but it will clear the path for a tested and well-known approach that can be used in many situations, like - for example - the one above.

Here are the main advantages:

  • it decouples the interface from the implementation

  • it creates a single point, the factory method, where the coupling between the interface and the implementation happens

  • it adds extensibility to your program design: not only can you add behaviour to your application, you can change it at runtime!

To explain how the Factory Pattern (and the Abstract Factory Pattern, to be precise) works, I will use the rudimentary game engine, shown in Listing 1. It is by far not a running program, but will serve the purpose to show how a "not so flexible" design can be changed into a more flexible one.

The source code examples are written in Delphi, but any programmer should pick it up quickly: When you read .Create, think of the new-operator and don't get confused by the begin - end - blocks, they're just structural blocks ({ ... } - expression in Java or C++). Classnames in Delphi usually begin with a 'T' (for 'type'), interfaces with a 'I'.

Example: Sprites and Platforms

Take a look at Listing 1. It shows the game's level logic that creates one big monster and one small monster after 25 seconds. Two minutes later a big blue monster shows up (just ignore the question how this could ever be a working game. It's all about principles!). We're using the classes TBigMonsterSprite and TSmallMonsterSprite for the sprites and the game should run on a standard Windows system.

Listing 1 already shows a tight coupling between the game logic and the creation of the sprite objects. Now: what if we have to add a new sprite with a slightly different behaviour, for example for a new level? What if we have to swap out all the sprite objects because your company decided that they have to move into the games-market for mobile devices? We can see that Listing 1 isn't ready for this kind of requirement!

Listing 1. Tlevel1.Run procedure

procedure TLevel1.Run( TimeElapsedInSec: integer );
var
s1, s2: TBigMonsterSprite;
s3, s4: TSmallMonsterSprite;
begin
// after 25 seconds, create two new monsters:
if TimeElapsedInSec = 25 then
begin
//create a new sprite of class TBigMonsterSprite:
s1 := TBigMonsterSprite.Create();
s1.X := 100;
s1.Y := 200;
// more initialization
s1.Run;
s2 := TSmallMonsterSprite.Create();
s2.X := 150;
s2.Y := 100;
// more initialization
s2.Run;
end;
// after 145 seconds, create a blue monster:
if TimeElapsedInSec = 145 then
begin
s1 := TBigMonsterSprite.Create();
s1.X := 100;
s1.Y := 200;
s1.Color := clBlue;
// more initialization
s1.Run;
end;
//put more usefull code here!
end;

procedure TGame.Run;
var
level: TLevel1;
timeElapsedInSec: integer;
begin
//do some initialization of the game engine here
timeElapsedInSec := 0;
//create level 1:
level := TLevel1.Create();
repeat
//call the level logic:
level.Run( timeElapsedInSec );
//put code that runs the game engine here
//increase timeElapsedInSec...
until GameOver or Win;
end;

To avoid this embarrassing situation, we have to find out what parts of the code will be likely to change in the future. In our case, we have sprites that represent big monsters in the desktop version of the game as well as the same sprites for the mobile platform. In addition, we need to create sprites that show the small monsters on both platforms.

To start with, we extract an interface for all the different sprite classes, which is shown in Listing 2, and change the code that creates the code (the "client") so it uses the sprite interface. After that we create the new methods CreateBigMonsterSprite and CreateSmallMonsterSprite, which return new ISprite objects, according to their names. We then redirect the calls to TBigMonsterSprite.Create and TSmallMonsterSprite.Create to our new methods (Listing 3).

Listing 2. An interface for all the different sprite classes

type
//this interface represents a sprite in the game:
ISprite = interface
property X: integer;
property Y: integer;
property Color: TColor;
procedure Run;
procedure Freeze;
procedure Die;
end;

Listing 3. New method

procedure TLevel1.CreateBigMonsterSprite: ISprite;
begin
result := TBigMonsterSprite.Create();
end;

procedure TLevel1.CreateSmallMonsterSprite: ISprite;
begin
result := TSmallMonsterSprite.Create();
end;

procedure TLevel1.Run( TimeElapsedInSec: integer );
var
s: ISprite;
begin
// after 25 seconds, create two new monsters:
if TimeElapsedInSec = 25 then
begin
//create a new sprite with a call to the factory method:
s := CreateBigMonsterSprite();
s.X := 100;
s.Y := 200;
// more initialization
s.Run;
s := CreateSmallMonsterSprite();
s.X := 150;
s.Y := 100;
// more initialization
s.Run;
end;
// after 145 seconds, create a blue monster:
// ...
end;

Now the coupling between game level and sprites is already less tight, thanks to the sprite interface and the two methods called "factory methods". If we want to, we can now switch sprites for different platforms by adding some conditional statements to CreateBigMonsterSprite and CreateSmallMonsterSprite, but that's not the way the Factory Pattern wants us to do it.

Instead, the Factory Pattern uses inheritance to create its different "products": Figure 1 and Listing 4 show how we must change our code to get full extensibility in supporting our game on many platforms. The two factory methods are now abstract and will be implemented in the concrete classes TLevel1Desktop and TLevel1Mobile. The level's game logic will remain in the static method Run of TLevel1 and is now completely platform-independent. The decision, which platform to use lies in the code of the main game class where the concrete level class is instantiated. As we want to support two platforms we now need four sprite classes, two for each platform.

A d v e r t i s e m e n t
Linux BSD Unix ranking vote

Page: 1 2
Buy article Buy subscription
Buy now add to cart
add to cart
Standard price: 2€/$3 Standard price: 25€/$30
Buy article for as little as (2€/$3) each allow access to individual articles. Buy a full access to our Software Developers's Journal archive portal. You will be able to read the articles from all archive issues from year 2005 and 2006. For just 25€/$30 you get unrestricted access to the entire website for the whole year.
SDJhakin9

.SDJ Users:


.:Login
.:Password

[Register]
[Forgotten your password?]

...Shopping Cart

sum: 0 €
Choose currency:

...Topics

...Advertisement

www.acunetix.com www.verifysoft.com

...Conferences




...Print Edition Archive

...Affiliate Program



 

 

Subscribe | Contact Us | Newsletter | Privacy policy | Regulations | See all issues | About SDJ
Copyright C 2006 by Software Developer's Journal. All rights reserved.