ThinkGeek - Cool Stuff for Geeks and Technophiles

September 29, 2004

An example of the abstract factory pattern

This is one of the creational patterns, whose intent is to provide an interface for creating families of related or dependent objects without specifying their concrete classes.

I’ll try to explain it using a "real life" example. Let’s suppose I want to decorate my house. And let’s suppose that there are only two different furniture shops in the town I live. One of them sells modern designs, and other one sells classic furniture. And let’s suppose again, that I’m only going to buy a door and a tv set ( I don’t have too much money ). And, finally, let’s suppose that I can buy the same items in both shops ( doors and tv sets ).

So, if I want to buy a tv set, I just have to any of the shops and ask for one tv set ( and pay it, of course ). And after having it properly installed at home, I know I can do some certain things with my tv set ( switch it on and off, pump the volume up,… ). And those actions could be done both with a modern tv and with and old one, although could be applied in a different way.

Well, I know I can go to the shop ( the modern one or the classic one, depending on what I exactly want ), but I could also pay someone to go and buy my tv and bring it home. I will only have to say "please, bring me a modern tv", and he / she should know that, to buy a modern tv, he / she should go to the “modern” shop and pay for a tv set. This guy / gal is the abstract factory. I don’t care how or what he / she does to bring a modern tv, I just want me tv now.

Before the as2 implementation, it’s time to point out something important. ActionScript doesn’t implement abstract classes. Sure, I can write a class that acts as an abstract class, but the fact is that the language doesn’t support them. So, although the GoF book says that the abstract factory and the factories should extend an abstract class, my implementation will be based on interfaces.

Well, back to code. If I go to the shop myself, or if I ask someone to bring me a tv, I just have to say “I want a tv”. So, both the shops and the guy that buys the tv will implement a common interface ( just to say it clearly, they should extend an abstract class, if possible, but as actionscript doesn’t implement them, they will just implement the same interface ).

Here is the interface:

import net.designnation.patterns.AbstractFactory.*

interface net.designnation.patterns.AbstractFactory.IFactoryActions
{
 public function getTV( ): ITVActions;
 	
 public function getDoor( ): IDoorActions;
}

There are two actions: getTV and getDoor ( "bring me a tv", and "I want a new door" ).

This is the code for the two shops:

import net.designnation.patterns.AbstractFactory.*

class net.designnation.patterns.AbstractFactory.ModernShop implements IFactoryActions
{
 function ModernShop( )
 {
  trace( "I've picked a modern shop" );
 }
 
 public function getTV( ): ITVActions
 {
  return new BrandNewTV( );
 }
 	
 public function getDoor( ): IDoorActions
 {
  return new ModernDoor( );
 }	
}

import net.designnation.patterns.AbstractFactory.*

class net.designnation.patterns.AbstractFactory.ClassicShop implements IFactoryActions
{
 public function ClassicShop( )
 {
  trace( "I've picked a classic shop" );
 }
 
 public function getTV( ): ITVActions
 {
  return new OldTV( );
 }
 	
 public function getDoor( ): IDoorActions
 {
  return new ClassicDoor( );
 }
}

Both classes ( shops ) implement the same interface : IfactoryActions ( in other words, they implement two methods: getTV, and getDoor ).

Let’s take a closer look at the getTV method:

public function getTV( ): ITVActions
{
 return new OldTV( );
}

Are we creating an instance of the OldTV class, but this method returns an interface?. Sure. That’s what lets us manage all the TV sets the same way. We won’t have to care about where we bought the tv (if it is modern or classic). We just will know that we can do some actions ( switch it on,… ). So, the two classes that represent the two kinds of tv sets will implement the same interface:

interface net.designnation.patterns.AbstractFactory.ITVActions
{
 public function pumpUpTheVolume( );
 public function shutUp( );
}

The modern TV will be:

import net.designnation.patterns.AbstractFactory.*

class net.designnation.patterns.AbstractFactory.BrandNewTV implements ITVActions
{
 function BrandNewTV( )
 {
  trace( "I've received is my new 42 inches plasma TV" );
 }
 	
 public function pumpUpTheVolume( )
 {
  trace( "sure, let me find the remote..." );
 }
 	
 public function shutUp( )
 {
  trace( "By pressing down this key, the sound dies" );
 }
}

And the classic one:

import net.designnation.patterns.AbstractFactory.*

class net.designnation.patterns.AbstractFactory.OldTV implements ITVActions
{
 function OldTV( )
 {
  trace( "I've bought an old TV" );
 }
 	
 public function pumpUpTheVolume( )
 {
  trace( "My old tv has no remote, so I must wake up and pumpUpTheVolume myself" );
 }
 	
 public function shutUp( )
 {
  trace( "My old tv has no remote, so........" );
 }
}

And will do the same with the doors. If the door is a modern one or a classic one, there will be the same actions: opening and closing it. So, now, the common interface that both classes will share is:

interface net.designnation.patterns.AbstractFactory.IDoorActions
{
 public function open( );
 public function close( );
}

The modern door:

import net.designnation.patterns.AbstractFactory.*

class net.designnation.patterns.AbstractFactory.ModernDoor implements IDoorActions
{
 function ModernDoor( )
 {
  trace( "ModernDoor constructor" );
 }
 	
 public function open( )
 {
  trace( "the modern door is opened" );
 }
 	
 public function close( )
 {
  trace( "the modern door is closed" );
 }
}

The classic one:

import net.designnation.patterns.AbstractFactory.*

class net.designnation.patterns.AbstractFactory.ClassicDoor implements IDoorActions
{
 function ClassicDoor( )
 {
  trace( "ClassicDoor constructor" );
 }
 	
 public function open( )
 {
  trace( "the classic door is opened " );
 }	
 	
 public function close( )
 {
  trace( "the classic door is closed" );
 }
}

Well, it’s the moment to stop, and look back. We have two shops. Both shops sell doors, and tv sets. The classic shop sells classic doors and tv sets, and the modern one sells moderns TVs and doors.

The guy we send to the shop to bring us a tv will be the abstract factory:

import net.designnation.patterns.AbstractFactory.*

class net.designnation.patterns.AbstractFactory.AbstractFactory 
{
 public static var MODERN	: Number = 1;
 public static var CLASSIC	: Number = 2;
 	
 public static function getFactory( shopType: Number ): IFactoryActions
 {
  if ( ( shopType & MODERN ) == MODERN )
  {
   return new ModernShop( );
  }
  		
  if ( ( shopType & CLASSIC ) == CLASSIC )
  {
   return new ClassicShop( );
  }
 }
}

This class implements the same interface that both ModernShop and Classic shop implement. So, I will only have to say: “hey, abstract factory, bring me a modern tv”, and I could forget about the details of the process. And the end of the process I’ll have a tv set, but I will not know how it came home, where it was built, or sold…. But how?. Look:

import net.designnation.patterns.AbstractFactory.*

var factory: IFactoryActions = AbstractFactory.getFactory( AbstractFactory.MODERN );

var myTV: ITVActions = factory.getTV( );
myTV.pumpUpTheVolume( );

var myDoor: IDoorActions = factory.getDoor( );
myDoor.close( );

You can download the code here

Posted by Cesar Tardaguila Date: September 29, 2004 09:10 AM
Comments

Great discussion of this design pattern. What is the 'GoF' book though?

Posted by: Joe Martinez en: September 29, 2004 04:45 PM

The GoF book is reffered to as an acronym for "Gang of Four" this refers to the authors of this book. Although this is not the only book to cover design patterns it is usually one of the first books that gets referenced when Design Patterns are talked about.

Posted by: Simeon en: September 29, 2004 05:53 PM

Sweet. That is the book I have. I've just not heard it referred to as GoF.

Posted by: Joe Martinez en: September 30, 2004 05:16 AM