Friday, July 8, 2011

Abstract with caution

Abstracts,

Interfaces, Factory Pattern these are fancy words that get thrown around by
programmers on a regular basis. The only
word I hear more is “Refactor” which makes me want to kick puppies but it is
part of the lingo.


An abstract class is essentially a base description. It would look something like so:


//AbstractFoo a base class for

stuff that likes to Bar()

class AbstractFoo

{

virtual void Bar(int a_nID) = 0;

};


Now the highbrow definitions of Abstract Classes and
Interface Classes says that an abstract has at least one pure virtual function
like Bar(int a_nID) = 0; the = 0 bit is
the thing that makes it pure virtual. I
weep if you don’t already know that. An
Interface class has no data members and fancy smanchy boys will prefix an
interface class with the letter “I” like IAmAnInterfaceClass, or
IICaptain. Abstracts can’t be
instantiated, we can’t ever have a new AbstractFoo(), its not allowed, no Foo
for You! You can instantiate interfaces
until your blue in the face, but a caveat is that since they have no data
members you should be aware of your allocations in the multitude of functions
that your interface contains. I had an
instance once where a debug interface was allocating 1 meg from the stack every
frame as a string buffer, this worked fine and was only debug so who cares? I
care. I had to port it to another
platform that was a little shy on the memories and proceeded to explode any
time it tried to allocate that handy meg of stack it did not have.


Abstracts are fantastically useful, the flagship of
polymorphism. Polymorphism to me has
always sounded like a fake word, like part of a magic spell Dr. Strange would
call out in battle. The idea behind the
abstract is that it describes a “thing” that you will use that performs common
functions. Its extensible because you
can swap out the “thing” with a different “thing” with relative ease provided
your replacement “thing” has the same functions.


Here’s a simple example.


Drinker.h

class Drinker : public AbstractFoo

{

virtual void Bar(int a_nID);

};


Drinker.cpp

void Drinker::Bar(int a_nID)

{

if ( a_nID == eBarsWeLike::Cheap )

{

//do drink

}

}


Alcoholic.h

class Alcoholic : public
AbstractFoo

{

virtual void Bar(int a_nID);

};


Alcoholic.cpp

void Alcoholic::Bar(int a_nID)

{

//do drink

}


See the differences?
The derived classes MUST implement the pure virtual function in the
Abstract lest they face the wrath of compiler errors and general whinging. The virtual keyword in the derived class is purely there to remind us that this function came from a virtual declaration
somewhere and also that classes derived from the derived class can override
should they choose to (but don’t I won’t like you if you do that). So we have two different “thing” classes that
both perform a function that is described in our Abstract. We use this in this way:


AbstractFoo* pFooBase = new Drinker();

pFooBase->Bar(1);


Sneaky huh? We
instantiated a new Drinker but we’re pointing to it with the Abstract
type. We can’t instantiate the abstract
but we can use pointers. The Polymorphism is really the compatibility between
the base and derived pointer, we can use one pointer of the base class to call
functions on many different types of derived objects.


AbstractFoo* pFooBase = new Alcoholic();

pFooBase->Bar(57);


We just changed the derived type to Alcoholic and called the
same function! The interchangeability is staggering! Its also very handy when we
have collections of several derived types, our collection would be base
AbstractFoo pointers and we simply call the function on each one:


AbstractFoo* BUKKIT[20]


For each AbstractFoo* pFoo in
BUKKIT

pFoo->Bar(1)

Its tasty, its made of win. Its something you already know right? Then welcome to club awesome, sit down and
relax.
I have come to realize that this pattern is

really not that useful for certain circumstances. I work with Audio a lot, I work with engine
code a lot too. Every game engine I have
touched in recent years has employed this pattern for things like Audio,
Physics, and GUI. Stop it. Now.
Today. It really isn’t necessary. In the world of game development for audio
there are 3 libraries (yes there are more but they are really bad). I have had to switch these libraries exactly
3 times in the last 10 years. The core
functionality of each of these libraries is so fundamentally different that it
was a complete waste of time to add the extra layer of an Abstract. They are not simple cut and paste replacement
jobs. Same with Physics. 3 libraries, very different animals.


The lesson here is that an Abstract is cool,
but its like Fonzy cool, you like hanging out with him but you’ll punch him in
the face if he goes near your sister.
You need to pick your battles when an abstract presents itself. Is there enough common functionality? How alike are the various source chunks that
might fit the abstract. If you find
yourself adding a bunch of pure virtual functions to the abstract then you
probably don’t need one. Just jam that new
tech in there and giv’r.


C. : public AbstractDude















No comments:

Post a Comment