//
Example 1.1
// Original
int IsFree(int Num, int Mode)
{
int RangeX, RangeY;
for(int i = 1; i <= g_MaxUnit; i++)
{
if(i !=
Num)
{
switch(Mode)
{
case
NX_NEAR_ALL:
RangeX
= g_Units[Num].Width + g_Units[i].Width;
RangeY
= g_Units[Num].Height + g_Units[i].Height;
break;
case
NX_NEAR_ENEMY:
case
NX_NEAR_ENEMYDANGER:
RangeX
= g_Units[i].Range + g_Units[i].Width + g_Units[Num].Width;
RangeY
= g_Units[i].Range + g_Units[i].Height + g_Units[Num].Height;
break;
default:
assert(false);
break;
}
if((abs(g_Units[i].X
- g_Units[Num].X) < RangeX) &&
(abs(g_Units[i].Y
- g_Units[Num].Y) < RangeY))
{
if(Mode
== NX_NEAR_ALL) return i;
if((Mode
== NX_NEAR_ENEMY) &&
(g_Units[i].Civilization]
!= g_Units[num].Civilization)) return i;
if((Mode
== NX_NEAR_ENEMYDANGER) &&
(g_Units[i].Civilization
!= g_Units[num].Civilization) &&
(g_Units[i].Force
> 0)) return i;
}
}
}
return 0;
}
[return
to article]
//
Example 1.2
// Version with meaningful names added
int GetNearUnit(int MyUnitIndex, int AcceptableType)
{
int RangeX, RangeY;
const SUnit& MyUnit=g_Units[MyUnitIndex];
for(int OtherUnitIndex = 1; OtherUnitIndex
<= g_CurrentUnitCount; OtherUnitIndex++)
{
const
SUnit& OtherUnit=g_Units[OtherUnitIndex];
if(MyUnitIndex
!= OtherUnitIndex)
{
switch(AcceptableType)
{
case
ANY_UNIT:
RangeX
= MyUnit.Width + OtherUnit.Width;
RangeY
= MyUnit.Height + OtherUnit.Height;
break;
case
ENEMY_UNIT:
case
DANGEROUS_ENEMY_UNIT:
RangeX
= OtherUnit.Range + MyUnit.Width + MyUnit.Width;
RangeY
= OtherUnit.Range + MyUnit.Height + MyUnit.Height;
break;
default:
assert(false);
break;
}
const
int SeperationX=abs(OtherUnit.XPos - MyUnit.XPos);
const
int SeperationY=abs(OtherUnit.YPos - MyUnit.YPos);
if
( (SeperationX<RangeX) &&
(SeperationY<RangeY))
{
if(AcceptableType
== ANY_UNIT) return OtherUnitIndex;
const
bool OtherIsEnemy=(OtherUnit.Civilization] != MyUnit.Civilization);
if((AcceptableType
== ENEMY_UNIT) && OtherIsEnemy) return OtherUnitIndex;
const
bool OtherIsDangerous=(g_Units[OtherUnit].Force > 0);
if((AcceptableType
== DANGEROUS_ENEMY_UNIT) && OtherIsEnemy && OtherIsDangerous)
return OtherUnitIndex;
}
}
}
return 0;
}
[return
to article]
//
Example 2.1
// Bad
void
ProcessEverything(void) {
g_Camera.Process();
}
void
CCamera::Process(void) {
//
lots and lots of code
g_Player.rotY+=SomeMemberVariableThatGetsTheEffectWeWant;
//
lots more code
}
[return
to article]
//
Example 2.2
// Still bad, but a lot more visible!
void
ProcessEverything(void) {
g_Camera.Process();
//
PW-HACK for Alpha!
g_Camera.HACK_AlterPlayersYRotation();
}
void
CCamera::HACK_AlterPlayersYRotation(void) {
g_Player.rotY+=SomeMemberVariableThatGetsTheEffectWeWant
;
}
void
CCamera::Process(void) {
//
lots and lots of code
// no hack hidden away here, it's
at a higher, more visible level, much more likely to get fixed
// lots
more code
}
[return
to article]
//
Example 3.1
// Classic example of problems
class C3DTriangle {
// some data
void Draw(void);
};
// This interface
instantly cripples performance, no matter what the implementation
is like
// Modern graphics architectures achieve their performance by dealing
with large groups of
// triangles at once, it'd be almost impossible to do that with
this interface
[return
to article]
//
Example 3.2
// Better, but still probably unacceptable for games
class C3DTriangle {
// some data
};
class C3DTriangleRenderer
{
void DrawTriangleList(C3DTriangle*
const pList,int nTriangleCount);
};
// This interface is a lot better, but is still a bad fit for a
lot of rendering architectures
// The idea of a design like this is to hide platform specifics,
but once you start altering
// the interface to fit the platforms characteristics as you'd have
to here, you've lost any
// platform independence. All you end up with is an interface that
apes a particular API, and
// so doesn't work well on any other platforms, and just adds an
extra layer of complexity to
// the program to no good effect. Wrapping up an API doesn't give
you platform independence!
[return
to article]
|