flipCode - Tech File - Jeroen Bouwens [an error occurred while processing this directive]
Jeroen Bouwens
Click the name for some bio info

E-Mail: flipcode@jeroen.ws



   01/31/2000, Factories


Introduction

It's been quite a while since my last techfile entry, but I finally found the time to write another piece in my ongoing series about OOP :-). Today, I want to explain a bit more about factories, what they are and, more importantly, what good they are to 3D-coders.



Patterns

Last time I briefly mentioned design patterns. For those of you unfamiliar with the term, Design Pattern are hot in the OOP community. Design patterns are solutions to regularly occurring problems. These solutions were not specifically developed by a person to solve these problems, but rather they emerged over time as being good solutions. One of the accepted "rules" for a design-solution to become a pattern is that it must have been applied independently in at least three software-packages.

Patterns are discovered rather than developed. You probably noticed at some time in your programming-career that you had used the same kind of construction to solve a certain problem a number of times in a program. That is similar to how patterns are discovered: Someone is looking over designs, and notices that different people have used identical solutions for a certain kind of problem. Writing this down and publishing a pattern prevents others from re-inventing the wheel.

But enough about patterns in general. Let's focus on a pattern I consider very useful: Abstract Factory.



Abstract Factory

An abstract factory allows you to create families of related objects, without specifying their concrete classes. Let me explain: Suppose you have a 3D-engine that represents models as sets of XYZ-points (As most engines do). All works well, but you suddenly decide you want to be able to choose between the ordinary XYZ-representation and an advanced parametric representation. This means a lot of your routines will change, for example the rotate routine. Let's say there are four classes that need to change certain routines for this new representation, while the other classes remain unchanged. The classes that change are CModel, CLight, CCamera and CRasterizer. The logical course of action is to create four derived classes: CParamModel, CParamLight etc. At a number of places in your code you create instances of these classes using new. How do you determine which class to instantiate at these places, especially if you want to have the choice between the two representations, or if you want to mix them? One way is to make a switch-case at every instantiation-place:

	switch (Representation)
	{
		case XYZ : 
                       Model = new CModel(); 
                break;
		case Parametric:
			Model = new CParamModel();
		break;
        }
This works, but I think you will agree it is not very convenient. Another solution is to make a factory-class with a creation method for every class that may change in the future:

class CFactory
{
	public:

	virtual CModel*        CreateModel()		{return new CModel()};
	virtual CLight*        CreateLight();		{return new CLight()}
	virtual CCamera*       CreateCamera();	        {return new CCamera()}
	virtual CRasterizer*   CreateRasterizer();	{return new CRasterizerl()}
}
Switching to another representation means using a derived factory:

class CParamFactory : public CFactory
{
	public:

	virtual CModel* 	CreateModel()		{return new CParamModel()};
	virtual CLight* 	CreateLight();		{return new CParamLight()}
	virtual CCamera*	CreateCamera();	        {return new CParamCamera()}
	virtual CRasterizer*	CreateRasterizer();	{return new CParamRasterizerl()}
}
You class creating members of one of these classes needs a factory as member. Wherever you would normally use new, you now call one of the creation methods of a factory. Since the interfaces of the Param classes remains the same (Well, they should anyway), this works perfectly (Remember the inheritance part from last time?).

The big advantage is that you can leave existing code alone. Simply assign another factory to a class (1 assignment), and it will create instances of the correct class without any modification to its code. Keep in mind that this is a very simple example. The abstract factory becomes really interesting for applications with more than two variations (eg XYZ, parametric and spherical coordinate representations) and in applications where the factory can change at runtime (Mix XYZ and parametric representations).

For completeness, here is the UML notation diagram for this pattern:




For those of you unfamiliar with this notation: a rectangle is a class, a triangle means inheritance, an arrow with a solid line means "has a member of class…", and an arrow with a dashed line means "creates an instance of ….". So the Client has a member of class CFactory, and CFactory creates an instance of CLight.

That's it for now. I have no idea when my next update will be. I'm in the process of starting up a company, so I'm quite busy, as you can imagine.

Happy coding,

Jeroen





  • 04/01/2002 - Thoughts On Software Development
  • 09/10/2000 - Observe Your Objects
  • 01/31/2000 - Factories
  • 09/14/1999 - Polymorphism and Inheritance
  • 08/14/1999 - Object Design
  • 06/04/1999 - OpenGL Mania
  • 05/04/1999 - 3D Clipping
  • 04/29/1999 - Introduction

  • This document may not be reproduced in any way without explicit permission from the author and flipCode. All Rights Reserved. Best viewed at a high resolution. The views expressed in this document are the views of the author and NOT neccesarily of anyone else associated with flipCode.

    Download The Sample Program: ManicMotion.exe

    [an error occurred while processing this directive]