venerdì 30 dicembre 2011

Programmazione: Ecco perché dovresti usare i private accessors

Ah, gli accessors. Per molti programmatori in erba sono quasi degli sconosciuti. Per i più navigati (e pigri) poco più che delle seccature. Per alcuni IDE, un modo come un altro per generare warnings e riempirvi il codice di fastidiose sottolineature.

E' tuttavia pratica comune ammettere che gli accessors pubblici hanno un loro perché, tirando fuori il principio di Incapsulamento dell'OOP. Ma cosa si può dire di un accessor privato?

Prendiamo ad esempio il seguente frammento di codice C++:

class Player
{
   public:
   
   void hurt(int damage){
      this->hp -= damage;
   }

   void heal(int amount){
      this->hp += amount;
   }

   private:

   int hp;
}

Il membro hp è privato e la classe Player vi accede in modo diretto all'interno dei suoi metodi. Che c'è di male? Beh, niente. Tutto lecito. Il compilatore va avanti e il programma funzia. Ma vediamo come apparirebbe con l'utilizzo di accessors privati:

class Player
{
   public:
   
   void hurt(int damage){
      setHp(getHp()-damage);
   }

   void heal(int amount){
      setHp(getHp()+amount);
   }

   private:

   int hp;

   private:

   void setHp(int value){
      this->hp = value;
   }

   int getHp(){
      return this->hp;
   }

}

E' vero, c'è del codice in più. E' vero, codice in più == bug in più. Ma ci sono anche ottimi motivi per operare in questo modo. Elenchiamoli:

  • Tutte le modifiche in un punto solo
    Sappiamo che questo vale per gli accessors pubblici, perché non dovrebbe valere per quelli privati? Ogni modifica al membro hp verrà fatta dentro setHp(), riducendo la complessità di debug e manutenzione del codice (un solo breakpoint).
  • Validazione
    E' possibile operare logiche aggiuntive in un unico punto. Ad esempio si potrebbe controllare che gli hp non scendano sotto lo zero, o che non salgano sopra una certa soglia, mettendo tutti i controlli nella setHp() anziché spargerli nelle varie hurt(), heal() e così via.
  • Unit test?
    Con qualche accorgimento (tipo un metodo wrapper pubblico sugli accessor privati, magari da commentare dopo la fase di test) possiamo scrivere dei test più sicuri ed efficienti, perché andranno a spremere con tutte le logiche possibili un unico metodo. (I moderni tool di unit testing solitamente hanno poi escamotage per raggiungere anche i metodi privati, senza chiederci di sporcare il codice con wrapper vari.)
  • Incapsulamento
    Ancora una volta, l'essere privato non elimina i vantaggi del pubblico. E' vero, stiamo "nascondendo" dettagli implementativi alla stessa classe che li implementa, il che può apparire sciocco. Ma non è mai sciocco racchiudere funzionalità operanti su un membro in un unico punto.
  • Ridefinizione nelle classi derivate
    Ok, qui bariamo un attimo e rendiamo i nostri accessors virtual e protected. Le classi derivate possono ridefinire l'accesso al membro. Se questa esigenza fosse sorta dopo un po' e non avessimo avuto gli accessors privati saremmo stati costretti quantomeno ad un pericoloso refactoring.
  • Undo e Redo
    Altro figlio dell'avere le modifiche in un solo punto è la possibilità di aggiungere con facilità funzionalità di undo e redo.

I vantaggi qui elencati sono solo esempi. Scommetto che lavorandoci se ne potrebbero trovare altri.

Buon coding...e Buon Anno!

L'articolo originale da cui ho tratto lo spunto per questa riflessione è pubblicato sul blog Joel on Software

Nessun commento:

Posta un commento