Czytelny kod: Nazwy zmiennych

Uwaga! Informacje na tej stronie mają ponad 5 lat. Nadal je udostępniam, ale prawdopodobnie nie odzwierciedlają one mojej aktualnej wiedzy ani przekonań.

# Czytelny kod: Nazwy zmiennych

Sun
10
May 2009

W matematyce zawsze wkurzało mnie, że używa się oznaczeń jednoliterowych z ewentualnymi indeksami i trzeba pamiętać, co który symbol znaczy. W programowaniu nie jesteśmy tak ograniczeni i warto to wykorzystywać, aby poprzez dobre nazywanie zmiennych dbać o czytelność kodu.

Na przykład jeśli trzeba napisać dwie pętle, jedna w drugiej, to ja nigdy nie nazywam zmiennych sterujących i, j. Jeśli jest tylko jedna pętla, to nie ma problemu z nazwaniem zmiennej i, a jeśli pętle przechodzą po pikselach bitmapy, to naturalne jest ich nazwanie x, y. W każdym innym przypadku wolę bardziej opisywe nazwy, bo wtedy trudniej o pomyłkę. Np.:

for (uint obj_i = 0; obj_i < ObjCount; obj_i++)
{
  const OBJECT &obj = *Objects[obj_i];
  for (uint vert_i = 0; vert_i < obj.GetVertexCount(); vert_i++)
  {
    const VERTEX &vert = obj.GetVertex[vert_i];
    vert.Pos = CalcPos();
    vert.TexCoord = CalcTexCoord();
  }
}

Zauważcie też, w jaki sposób do zmiennych pośrednich przypisałem referencje do obiektów, żeby nie odnosić się za każdym razem przez ich pełne kwalifikowanie. Gdybym tego nie zrobił, to te najbardziej wewnętrzne instrukcje musiałyby wyglądać tak:

Objects[obj_i]->GetVertex[vert_i].Pos = CalcPos();
Objects[obj_i]->GetVertex[vert_i].TexCoord = CalcTexCoord();

Jeszcze jedna zaleta jest taka, że w przypadku gdybym zmienił sposób odwołania do obiektów czy wierzchołków (np. nazwa metody GetVertex), w kodzie mojej funkcji wystarczy zmienić tylko jedno miejsce.

Czy to jest mniej optymalne? Nie sądzę. Nie zagłębiałem się co prawda w asembler, ale ogólnie uważam, że przechowywanie wyników pośrednich w osobnych zmiennych raczej służy wydajności, niż się z nią kłóci.

Przykład bardziej z życia wzięty, a konkretnie z mojego dzisiejszego kodu: Chciałem sobie wygenerować wierzchołki quada przedstawiającego na ekranie plamę o losowych parametrach, która jest tym większa, im znajduje się dalej od środka ekranu - coś w rodzaju współrzędnych biegunowych. To dobrze współgra z ludzkim postrzeganiem, bo myślę, że wzrok człowieka też działa na współrzędnych biegunowych. To by tłumaczyło, dlaczego jaskra zaczyna się od zawężania pola widzenia i dlaczego efekt tunelu wydaje się tak "psychodeliczny" :)

Wracając do tematu, oto kod:

COLORF Color_f;
ColorRainbow(&Color_f, g_Rand.RandFloat());
COLOR Color = ColorfToColor(Color_f);

VEC2 TexCoordBias = VEC2(
  g_Rand.RandBool() ? 0.5f : 0.0f,
  g_Rand.RandBool() ? 0.5f : 0.0f );

VEC2 ScreenSize = VEC2(
  (float)frame::Settings.BackBufferWidth,
  (float)frame::Settings.BackBufferHeight );
VEC2 CenterPos = VEC2(
  g_Rand.RandFloat() * ScreenSize.x,
  g_Rand.RandFloat() * ScreenSize.y );
float Orientation = g_Rand.RandFloat(PI_X_2);
float DistFromScreenCenterFactor =
  DistanceSq(CenterPos, ScreenSize*0.5f) / LengthSq(ScreenSize*0.5f);
float Size =
  ScreenSize.y * DistFromScreenCenterFactor * g_Rand.RandFloat(0.1f, 0.3f);
float Aspect = g_Rand.RandFloat(0.7f, 1.3f);
VEC2 RightVec = VEC2( cosf(Orientation), -sinf(Orientation) ) * Size;
VEC2 UpVec; Perpedicular(&UpVec, RightVec);
RightVec *= Aspect;

VEC2 Pos;
m_SplatQuads.resize(m_SplatQuads.size()+1);
SPLAT_QUAD &Quad = m_SplatQuads[m_SplatQuads.size()-1];
Pos = CenterPos - RightVec - UpVec;
Quad.Vertices[0].Pos = VEC4(Pos.x, Pos.y, 0.5f, 1.f);
Pos = CenterPos + RightVec - UpVec;
Quad.Vertices[1].Pos = VEC4(Pos.x, Pos.y, 0.5f, 1.f);
Pos = CenterPos - RightVec + UpVec;
Quad.Vertices[2].Pos = VEC4(Pos.x, Pos.y, 0.5f, 1.f);
Pos = CenterPos + RightVec + UpVec;
Quad.Vertices[3].Pos = VEC4(Pos.x, Pos.y, 0.5f, 1.f);
Quad.Vertices[0].Tex = VEC2(TexCoordBias.x,      TexCoordBias.y);
Quad.Vertices[1].Tex = VEC2(TexCoordBias.x+0.5f, TexCoordBias.y);
Quad.Vertices[2].Tex = VEC2(TexCoordBias.x,      TexCoordBias.y+0.5f);
Quad.Vertices[3].Tex = VEC2(TexCoordBias.x+0.5f, TexCoordBias.y+0.5f);
Quad.Vertices[0].Diffuse = Color;
Quad.Vertices[1].Diffuse = Color;
Quad.Vertices[2].Diffuse = Color;
Quad.Vertices[3].Diffuse = Color;

Dzięki użyciu tak wielu jasno nazwanych zmiennych pośrednich kod jest nie tylko czytelny i samoopisujący się, ale i wydajny. Wartości takie jak ScreenSize wyliczam tylko raz, a potem wiele razy używam. Gdybym wpisał ich wyrażenia wprost w miejsca gdzie są użyte, to kod byłby bardziej zaciemiony, a co do wydajności pozostałoby liczyć na optymalizacje kompilatora.

Comments | #c++ Share

Comments

STAT NO AD
[Stat] [STAT NO AD] [Download] [Dropbox] [pub] [Mirror] [Privacy policy]
Copyright © 2004-2019