venerdì 26 novembre 2010

L'importanza delle convenzioni dei nomi

E' innegabile il valore delle convenzioni applicate ad un progetto software di qualsiasi natura. Le convensioni aumentano la leggibilità delle risorse (tipicamente del codice), semplificano il refactoring, la manutenzione e in generale pongono le basi per l'automazione.
In un progetto database le convenzioni sul nome delle tabelle, procedure, trigger e delle colonne è ancora piu' importante: il database può facilmente crescere a dismisura, arrivando ad avere relazioni intricate fra oggetti diversi. Tuttavia occorre ricordarsi che un DBMS è differente da un sistema OOP, e quindi le convenzioni da usare su uno possono essere differenti e portare a differenti risultati rispetto a quelle usate nell'altro. Si prendano ad esempio i nomi delle colonne: in un sistema OOP i nomi degli attributi degli oggetti potrebbero assumere la stessa convenzione al fine di automatizzare l'accesso alle proprietà stesse, complice anche l'ereditarietà. In altre parole, avendo due oggetti come Person e Department ciascuno dotato di un codice identificativo (ID) e di una descrizione (description) si potrebbe pensare di dotare i due oggetti delle stesse proprietà getID/setID e getDescription/setDescription. Facile, comprensibile, omogeno. 
Nel caso di un database con due tabelle che implementino gli stessi concetti si potrebbe pensare a creare due tabelle con le stesse convenzioni sui nomi: person(ID integer, description text) e department(ID integer, description text). La cosa può a prima vista sembrare naturale, e per molte applicazioni lo è. Tuttavia quando si tenta di mappare i due schemi uno sopra all'altro, si scopre che la convenzione sul nome delle colonne appena scelto produce uno spreco di codice. Si immagini infatti di voler esprimere la relazione fra un reparto e la lista delle persone che vi afferiscono: l'oggetto Department dovrà avere una collection di tipo Person, e nel database ci sarà una tabella di join fra le due di cui sopra. Il problema è che nel join i dati di una tabella devono essere riscritti per non andare in conflitto (ambiguità) con quelli dell'altra tabella, e quindi si potrà avere un solo ID e un solo description nell'output della query, mentre l'altro diventerà ad esempio person_ID e person_description. E perché questo semplice aliasing porterebbe alla scrittura di maggiore codice? Perché i DAO usati per caricare un reparto e una persona fanno affidamento a colonne che si chiamano ID e description, che sono venute a mancare nel join. Come si può risolvere questa problematica? Ci sono tre metodi principali:
  1. si scrive un DAO per il caricamento diretto di un Person, di un Department e di un join fra i due. Ovviamente questa soluzione non è molto pratica;
  2. si creano delle viste su tutte le tabelle in modo che i dati ritornati siano sempre person_ID, person_description, department_ID, department_description sia per le query "semplici" che per i join. Ma questo richiede la scrittura di codice aggiuntivo e non molto utile;
  3. si usa una convenzione differente nei nomi delle colonne: ogni colonna viene nominata con un prefisso che corrisponde al nome della tabella: person( personID integer, personDescription text ) e department( departmentID integer, departmentDescription text ). Così facendo i DAO vengono scritti sempre per colonne che sono univoche nel sistema, anche quando le tabelle vengono messe in join.
L'ultima soluzione, seppur appaia la piu' farraginosa, è quella che porta alla scrittura del minor codice possibile e soprattuto garantisce che se un DAO funziona per la tabella singola funzionerà senza variazione alcuna anche per tabelle in join.

Nessun commento: