Stiluri de Programare

Un stil de programare este un set de reguli utilizate la scrierea codului. Acesta are scopul de a facilita înțelegerea programului și de a minimiza riscul apariției de buguri. Regulile pot varia de la limbaj la limbaj sau de la echipă la echipă. Este important ca acestea să fie respectate de toți participanții la proiect, pentru a-și putea înțelege munca unul altuia. Cu timpul veți observa că în proiectele mari acesta este strict necesar să aveți un stil de programare, deoarece acestea devin prea complexe pentru a fi înțelese ușor și pentru a le fi reținută funcționalitatea.

Convenții de denumire

Pentru denumirea variabilelor, funcțiilor, claselor, fișierelor, etc. este important să urmați o convenție de denumire concisă, din care să reiasă scopul precis al blocului denumit. Acestea ar fi câteva reguli generale de denumire:

  1. Toate denumirile ar trebui să fie descpriptive, dar concise.
void ComputePlayerHeight() {
   ...
}
  1. Abrevierile ar trebui evitate, dar nu eliminate. Variabilele cu nume lungi nu sunt o problemă, însă uneri ar putea fi ocolite.
uint32_t playerID;
  1. Numele ar trebui să fi scrise legat. În cpp nu sunt comune underscore-urile drept în mijlocul numelor.

  2. Constantele sunt de obicei scrise cu majuscule, cuvintele fiind în acest caz despărțite de underscore-uri.

const Transform DEFAULT_POSITION;
  1. Numele nu ar trebui să includă informație duplicată, atunci când este posibil.
enum PlayerStateEnum {
    // Name should be PlayerState
};

Indentare și Spații

Indentarea codului are scopul de a-l face mai citeț. Cu toate acestea, în alte limbaje precum python, aceasta este strict necesară. Pentru limbajul cpp există mai multe reguli de indentare și spațiere.

  1. Liniile libere servesc scopul de a separa grupuri logice din cod.
Colour GenerateRandomColour {
    uint32_t colourR = Random.Range(0, 255);
    uint32_t colourG = Random.Range(0, 255);
    uint32_t colourB = Random.Range(0, 255);
    float colourA = 1;

    Colour newColour = Colour(colourR, colourG, colourB, colourA);
    return newColour;
}
  1. Funcțiile ar trebui separate de 1-2 linii acolo unde au implementarea.
void Animal::UpdateHealthByIncrement(uint32_t increment) {
   ...
}

void Animal::UpdateHungerByIncrement(uint32_t increment) {
   ...
}
  1. Taburile ar trebui să aibă un număr constant de spații libere. Un număr comun este 3.
  2. Spațiile ajută la citirea mai ușoară a codului. Aici sunt câteva exemple de scriere absolut necesare:
Like this      a = (a + b) * c;
Not like this  a=(a+b)*c;

Like this      while (true) {}
Not like this  while (true){}

Like this      for (int i = 0; i <= n; i++) {}
Not like this  for (int i=0;i<=n;i++) {}


Like this      if(Condition()) { 
                   DoSomething(); 
                   ...
               }
               else {
                   DoNothing();
                   ...
               }

Not like this  if(Condition()) DoSomething();
               else DoNothing();

Alte Reguli

  1. Lungimea liniilor de cod ar trebui să nu depășească o măsură fixă. De obicei aceasta este de 80 de caractere.
Vector2D screenSize = Screen::GetScreenSize() * currentScreenSizeModifier -
      Vector2D(screenPaddingX * 2, screenPaddingY * 2);

Uneori, pentru a evita astfel de situații, e bine să mai creăm variabile. Acest lucru ajută și la lizibilitatea codului.

Vector2D maxScreenSize = Screen::GetScreenSize();
Vector2D screenPadding = Vector2D(screenPaddingX * 2, screenPaddingY * 2);
Vector2D frameSize = maxScreenSize * currentScreenSizeModifier - screenPadding;
  1. Consistency is key!



Un exemplu de reguli

Acestea au fost doar câteva reguli generale, care ar trebui respectate mereu când scriem în cpp. Cu toate acestea am pregătit un set de reguli pe care îl folosesc eu.

Variabile

  1. Numele variabilelor încep cu literă mică.
void HandleMovement(double deltaTime);
  1. Dacă variabilele aparțin unei clase, sunt precedate de un identificator, de exemplu m_ pentru membrii și s_ pentru membrii statici.
Vector3D m_size;
static Mesh s_mesh;
  1. Membrii claselor sunt privați, declarați la finalul definiției clasei.
class Player {
 ...
 private:
     State m_state;
     bool m_isHidden;
 ...
};
  1. Membrii claselor au un getter și un setter, fiind funcții inline din header.
class Player {
 ...
 public:
     inline State GetState() { return m_state; }
     inline void SetState(State state) { m_state = state; }
 ...
};

Funcții

  1. Numele funcțiilor încep cu literă mare.
void Update();
  1. Numele funcțiilor sunt scrise fără underscore-uri, fiecare cuvânt începând cu literă mare, folosind abrevieri unde este necesar.
void GenerateGameObjectID();
  1. Poziționarea acoladelor se respectă în tot proiectul: fie pe același rând cu declararea, fie pe rândul imediat următor.

  2. Blocurile de implementare a funcțiilor sunt separate de câte un spațiu acolo unde aparțin aceluiași segment logic, putând adăuga mai multe spații pentru cele vare diferă în logică.

GameObject::GameObject() {
    ...
}

GameObject::~GameObject() {
    ...
}


void GameObject::InitMesh() {
    ...
}

Clase, structuri, enum-uri

  1. Numele încep cu literă mare.
class GameObject {

};
  1. Numele sunt scrise fără underscore-uri, fiecare cuvânt începând cu literă mare. Abrevierile ar trebui evitate.

  2. Declararea unei instanțe este complet separată de definire.

// Așa da
struct GameObject {

};

GameObject cube;


// În niciun caz așa
struct GameObject {

} cube;

Fișiere

  1. Numele încep cu literă mare.

  2. Numele pot fi scrise folosind underscore-uri, fiecare cuvânt începând cu literă mare.

    Game_Object.h

  3. Fiecare strcutură de date va avea câte un fișier corespunzător sau mai multe.

    TimeManager.h și TimeManager.cpp