Some Creational Design Patterns

Brioche/Aspirine

00 - Table of Contents

01 - Introduction
02 - Factory Method
03 - Abstract Factory
04 - Builder
05 - Singleton
06 - Other Creational Patterns

01 - Introduction

The way objects are created is an important step in their life cycle. In most case, instanciation will even influence the behaviour of the object, that's just why this critical operation must be well designed if you want your frameworks to be as flexible as an old-skool jelly vector cube.

In C++, you basically have two ways to instanciate classes:

- STATIC, objects are stored in the data segment or on the execution stack when the instanciation comes in the code.

- DYNAMIC, memory is runtime allocated by the "new" operator called in the code. This way allows late binding. But warning, since no garbage collection is done, you'll have to cope with memory deallocation issues.

Here are some examples of instanciations in C++:


	Foo foo1;
	Foo *foo2 = new Foo;
	Foo *foo3 = new Foo [100];

Most of the time, these basic instanciation schemes are sufficient to bring objects to life. But when you have to deal with complex object architectures, it may be useful to write classes which will behave as "object factories".

02 - Factory Method

The aim is to define an interface to create objects whose type is defined by sub-classes. This is pattern is also often refered as a Virtual Constructor (no connection with C++'s keyword "virtual").

The following (ugly) ASCII OMT-like chart shows the structure of the Factory Method Pattern.


	     ---------				    ---------
	    | Product | 			   | Creator |
	     ---------				    ---------
		 |					|
		/_\ . . . . . . Inheritance	       /_\
		 |					|
	 -----------------			-----------------
	| ConcreteProduct | <----------------- | ConcreteCreator |
	 -----------------	     .		-----------------
				     .
				 Instanciation

Example:

	// Creator.h

	class Product {
	public:
		virtual void common_behaviour();
	};

	class Creator {
	public:
		virtual Product *create_product( Product_id id );
	};

	// Creator.cc

	Product *Creator::create_product( Product_id id )
	{
		if ( id == ID_1 )
			return new Product1(  );
		if ( id == ID_2 )
			return new Product2(  );
		// ...
		if ( id == ID_N )
			return new ProductN(  );
	}

With this system, you can easily change the behaviour of the factory and extends the number of id's simply by subclassing the Creator class.

You can also multiply the number of factory methods if there is no common base. On the other hand, if you have a set of similar constructors (e.g. default constructors), you can use templates to build concrete creators:


     // We keep the Creator/Product classes from previous example

     template  class Generic_Creator : public Creator {
     public:
	     Product *create() { return new A_Product; }
     };

     // Force compiler to build a class to create Super_Product
     template class Generic_Creator Super_Creator;

Using factory methods also allows you to guarantee the way objects are create and it's usually a good place to write a robust error handling system. I used a bunch of Factory Methods to create the messages for a Networked application. That way, I was sure that no one could create weird messages since the only data structures allowed by my custom socket class was a Message object created by the Message Factory.

03 - Abstract Factory

The Abstract Factory Pattern is used to provide an interface to create a FAMILY of objects without having to specify their concrete (real) class.

Example: As you are a kind scener, you've decided to write portables demos for both Win32 and X11. So you need to deal with several objects.


			       --------------
			      |   Factory    |
			      |--------------|
			      |make_window() |
			      |make_palette()|
			       --------------
				     |
      instanciates		    /_\
	    .			     |
	     .		    +-----------------+
	      . 	    |		      |
	       .      --------------	--------------
		.    | X11_Factory  |  | Win_Factory  |
		 .   |--------------|  |--------------|
     X11_Window <----|make_window() |  |make_window() |----> Win_Window
    X11_Palette <----|make_palette()|  |make_palette()|----> Win_Palette
		      --------------	--------------


		  --------			-----------
		 | Window |		       |  Palette  |
		 |--------|		       |-----------|
		 |open()  |		       |set_color()|
		 |close() |		       |update()   |
		 |resize()|		       |invert()   |
		 |  ...   |		       |    ...    |
		  --------			-----------
		     |				     |
		    /_\ 			    /_\
		     |				     |
	       +------------+		      +---------------+
	       |	    |		      | 	      |
	  ----------	----------	 -----------	 -----------
	 |X11_Window|  |Win_Window|	|X11_Palette|	|Win_Palette|
	 |----------|  |----------|	|-----------|	|-----------|
	 |   ...    |  |   ...	  |	|    ...    |	|    ...    |
	  ----------	----------	 -----------	 -----------

The big thing about abstract factories is the client only deals with the abstract classes Window and Palette, only the choice of the factory will influence the type of the object created. Each concrete factory will create and setup the objects for the host windowing system without.

04 - Builder

The Builder Pattern splits the construction part of complex objects and the representation of these objects. Thus, a single construction process can create objects having different representation.

The Builder object is a member of the Client class.

You are the editor of a worldfamous popular diskmag (let's say it starts with an "H" and ends with "ugi") and you want to allows users to save the contents of the articles in several formats. The goal is to use the same interface to save the text than to display it on screen.


  ------------	 builder	 --------------
 |Text_Loader |<>-------------->|Text_Converter|
 |------------| 		|--------------|
 |parse_text()| 		|conv_char(c)  |
  ------------			|conv_font(fnt)|
				|conv_par()    |
				 --------------
				       |
				      /_\
		   +-----------------------------------------+
		   |		       |		     |
	     ---------------	 ----------------     -----------------
	    |ASCII_Converter|	| HTML_Converter |   |Screen_Converter |
	    |---------------|	|----------------|   |-----------------|
	  +-|char*get_text()| +-|char *get_page()| +-|pix *get_pixels()|
	  |  ---------------  |  ----------------  |  -----------------
	  |		      | 		   |
	  |   -------------   |   --------------   |   --------------
	  +->/ ASCII text /   +->/HTML document/   +->/Screen buffer/
	     -------------	 --------------       --------------

The code of the parse_text method should like this:

	while ( tok = get_next_token() ) {
		switch ( tok.type() ) {
		tok.CHAR:
			// print char
			builder->conv_char( tok.get_char() );
			break;
		tok.FONT:
			// change font
			builder->conv_font( tok.get_font() );
			break;
		tok.PAR:
			// paragraph mark
			build->conv_par();
			break;
		// ...
		}
	}

As you may have notice, there are no "Product" superclass. All the produced objects are different whatever they represent the same thing.

With this pattern, it's piece of cake to add another converter without having to change the whole conversion system and the core parsing routines.

05 - Singleton

A Singleton is a class for which you can only have ONE AND ONLY ONE instance. In a plain demomaker point of view, such objects includes device drivers or custom memory managers for instance. The main challenge in the design of the Singleton Pattern is that it must be possible to extend (i.e. to inherit from) the class.

To implement a Singleton in C++, we'll use several tricks we'll detail later. Let's check out the code:


	// Singleton.h

	class Singleton {
		static Singleton *unique_instance;
	protected:
		Singleton();
	public:
		static Singleton *get_instance();
	};

	// Singleton.cc

	// Init the static data member to null
	Singleton *Singleton::unique_instance = 0;

	// Returns the unique instance of the object
	Singleton *Singleton::get_instance()
	{
		if ( unique_instance == 0 )
			unique_instance = new unique_instance;
		return unique_instance;
	}

By protecting the default constructor, we prevent the Singleton from being instanciated by an external fellow. Only subclasses will be able to invoke that constructor. As you may have noticed, the Singleton is a self-constructed object. The Singleton will be constructed only if needed.

I personally used the Singleton pattern in a Network client written in Java. I had defined a Socket Proxy class which was a singleton so every piece of code that needed to read or write from the socket, just ask its own reference to the single object. It's far better to use this way rather than passing a reference to the single object to all client classes. It's absurd to have a Socket argument in the constructor of a dialog box!

I won't explain here how to subclass a singleton since it's quite a difficult operation but if anyone is interested in Design Patterns, I may write more stuff for the next issue of Hugi!

Other Creational Patterns

We've studied several useful Creational Patterns but there are others, like Prototype (the class of an object). I actually just hope I'll have a little more spare to make further investigation on the web and in books to find more Patterns!

brioche of the aspirine d-zign international demo brigade