Thursday, December 22, 2011

Bridge Pattern

Guess who's back? (bah~ I should stop with these monologues)

I'd like to write about something I've been using a lot these days doing my sound and graphics programming.
Not that it has anything to do with sound or graphics.
You probably saw in games that sometimes you could actually chose between OpenGL and DirectX (Deus Ex), or FMOD and XAudio2 (StarCraft 2 if I remember well). Well, this is possible thanks to the Bridge Pattern. This pattern lets us programmers switch from one implementation to another without having to change everything behind.

Imagine you're programming an application to create a window. You would code something close to:
Heretic programming

Ok, doesn't seem to be a lot of work... But keep in mind that each graphic API has over a hundred function (maybe more), for each functionality you have to do the same switch. And what will happen once Microsoft releases it's DirectX12? You will have to go through the code again and add the DirectX12 line and functionality. Lot of work for little readability and even less maintainability.

That's where the Bridge Pattern comes in. This pattern won't free you from the work of implementing the new API, but it will separate each part making it object-oriented, making it easy to read and maintain.
The basis of this pattern is simple actually. I'll change now from graphics to sound and try to explain you guys how I implemented OpenAL and XAudio2 APIs within my engine.

First I create a pure virtual class VxSoundDeviceImp which will serve as the interface for the APIs I want to use in my engine namely VxOpenALSoundDeviceImp and VxXAudio2SoundDeviceImp. The names may seem long and they are long actually but since it will be only for internal use it won't matter.
To force my API to implement a certain functionality I define all functions I want to implement as virtual pure, this way Play and Stop for instance will have to be present in both implementations. The others may not be present in both APIs if not vital.

Now that I have my implementations, I create yet another class VxSoundDevice to actually interact with my implementations. This class will contain an instance of VxSoundDeviceImp that I will cast to one of it's child classes (see Polymorphism) to use one implementation. All it does is calling the interface functions so that the polymorphism will call the correct one. See the UML for a overall view.

UML Bridge Pattern


The only class the user can instantiate is the VxSoundDevice class making all implementation stuff work in the shades. It is completely transparent for the user if he is using OpenAL or XAudio2. A call to Play will simply play the sound and that is all he as to know.
Here's a simple tutorial of what the Bridge Pattern look like for an user (don't mind the missing deletes...):
How to Bridge Pattern



Also, often people are confused between the Bridge Pattern and the Adapter Pattern because they basically do the same job. Correct. What you should keep in mind is that each pattern focus on a type of problem.The Bridge Pattern and the Adapter Pattern are for a totally difference usage!
To quote from this article by James W. Cooper:
At first sight, the Bridge pattern looks a lot like the Adapter pattern in that a class is used to convert one kind of interface to another. However, the intent of the Adapter pattern is to make one or more classes' interfaces look the same as that of a particular class. The Bridge pattern is designed to separate a class's interface from its implementation so you can vary or replace the implementation without changing the client code.

Let's look at a quick example of a Adapter Pattern to sort things out. Every programmer as heard of the word wrapper (wrapper interface, wrapper library, etc).
Now, imagine a native function form an C API called int factorial(int number). We want now to create the same function to use it in a C# application and another for a LUA application. For this we use the Adapter Pattern to create a common interface that will implement a more specific function adapted to each language. We would have a Int32 factorial(Int32 number) in C# and function factorial(number).
I prefer not to give an implementation because I'm not used to that pattern, nor to create wrapper libraries. Internet is plenty of useful tutorials and articles about this subject and I'm sure Google won't refuse one of your request.


And I'm done. Hope this article is readable and it helpful to someone!

Popular Posts