Coding Standard
Premessa:
le convenzioni su come scrivere codice sono importanti sia per gli sviluppatori, sia per il team, sia per il progetto, per molte ragioni:
- buona parte del codice scritto (indicativamente dal 70% al 90%) nella durata della “vita” del software dovrà essere mantenuto
- è molto difficile che il software sia mantenuto per la sua intera vita da chi ha scritto la sua prima versione
- le convenzioni aumentano la leggibilità del software, questo permette sia la risoluzione dei bugs più immediata, sia un aumento della velocità di comprensione della funzionalità
- vuoi sviluppare un prodotto di qualità
A seguire una code convention per il linguaggio Java.
Strutturazione del codice
Un nuovo package Java deve essere creato per ciascun gruppo di funzionalità correlate, inoltre le directories devono essere create come la convenzione dei nomi dei packages in java.
Sarebbe auspicabile che si crei un file con nome-del-package.txt oppure .html nel quale venga riassunto brevemente lo scopo del package e le classi, metodi funzionalità più importanti.
Anche se Java consente di avere più classi nello stesso file, è consigliabile avere la seguente corrispondenza: un file, una classe, ovvero ciascuna classe deve essere in un file separato, a meno che non si è sicuri che quella classe non pubblica venga utilizzata solo per quella esigenza specifica in quel file da un’altra classe.
Scrivere in tutti i files:
- il tag ‘$Id$’
- una lista della storia del file che comprende:
- la data
- l’autore
- le modifiche effettuate
- se un file ha più di una classe scrivi un commento che indica il nome di tutte le classi presenti nel file e il loro scopo
- dopo il commento scrivi il package, lascia una riga vuota e scrivi gli import
a seguire un esempio:
/** * Copyright (C) 2014, Nome dell'azienda * * $Id: MyExample.java ,v 1.1 2014/02/01 10:01 autore Exp $ * Data Autore Modifiche * Gen 30 2014 autore Creato * Gen 21 2014 autore Aggiunta di due nuovi metodi */ package demo; import java.util.Random;
Per le classi e le interfacce scrivere i commenti come da convenzione:
/** * Questa parte è il commento * */
Prima della classe dovrebbe esserci sempre un commento della classe stessa che descriva lo scopo ed eventualmente anche un esempio di utilizzo.
Utilizzare anche i seguenti tags:
/** * ... * @author * @version * @see nomedellaclasse#nomedelmetodo */
Ecco un esempio di un commento di una classe e il suo utilizzo:
/** * Questa classe rappresenta un dipendente * Un esempio di utilizzo: * <pre> * Dipendente dipendente = new Dipendente(); * dipendente.getSkills(); * </pre> * * @author Nome Cognome oppure <a href="mailto:nomecognome@azienda.it"> </a> * @version $Revision: 3.1 $ $Date: 2014/01/30 10:28:29 $ * @see Lavoratore * */ class Dipendente extends Lavoratore {... }
E’ consigliabile mantenere il seguente ordine di scrittura nelle classi:
- variabili della classe (public, protected, a livello di package, private)
- costruttori pubblici
- costruttori privati
- metodi pubblici
- metodi protected
- metodi con visibilità defaul
- metodi privati
- inner class
a seguire una tabella riassuntiva per i modificatori di accesso java quale tipo di visibilità hanno:
Modificatore | Class | Package | Subclass | World |
public | Si | Si | Si | Si |
protected | Si | Si | Si | No |
nessun modificatore | Si | Si | No | No |
private | Si | No | No | No |
Per la variabili della classe seguire il seguente ordine:
- public
- protected
- nessun modificatore
- private
la convenzione javadoc consente di descrivere lo scopo, l’utilizzo delle variabili di istanza e delle variabili static. Consente anche l’utilizzo dei tags HTML.
@see URL @see stringa
// Rappresenta il prezzo dell'item public double price;
Per i metodi la convenzione javadoc prevede la descrizione dello scopo, delle precondizioni, degli effetti, degli algoritmi, di come si utilizzano i metodi. Anche per i metodi è consentito l’utilizzo dei tags HTML.
@param nomeDelParametro descrizione del parametro @return descrizione del valore di ritorno @throws nomeDellaEccezione descrizione di quando si verifica l'eccezione @see stringa @see nomeDellaClasse#nomeDelMetodo
Per i commenti multilinea usare
/* commento con più linee */
Per i commenti di una sola linea usare
// commento di una sola linea
Ricorda il “perchè” e il “che cosa”.
Spesso i commenti descrivono ad esempio che cosa fa un metodo ma é bene descrivere nel commento dove opportuno, anche il perchè è stato fatto, è stato sviluppato quel metodo o quella classe.
Se l’algoritmo è complesso è bene descrivere l’algoritmo nel commento.
Layout
Il layout prevede l’indentazione, la lunghezza massima di ogni linea di codice e l’utilizzo delle parentesi graffe.
Per l’indentazione non utilizzare il tab ma 4 spazi per l’indentazione (oppure configurare l’IDE affinchè il tab abbia 4 spazi).
Impostare nell’ IDE la lunghezza massima di caratteri pari a 75 caratteri, questo consente una migliore leggibilità del codice. Questo dovrebbe valere anche per i commenti.
Per l’utilizzo delle parentesi graffe, solitamente si hanno una delle due seguenti convenzioni. Utilizzare la parentesi graffa di apertura nella linea dove viene definita la classe o il metodo oppure utilizzare la parentesi graffa di apertura all’inizio della linea successiva della definizione della classe o del metodo. Se si adotta quest’ultima convenzione le parentesi graffe saranno verticalmente allineate.
class MyClass { ... public double getSales() { if (condizione) { ... } else { ... } } ... }
lasciare una linea bianca tra i vari metodi (incrementa la leggibilità).
Dichiarare le variabili dei metodi all’inizio del metodo, ad eccezione per i cicli.
Spazi bianchi.
Utilizzare uno spazio bianco nei seguenti casi.
Dopo una parola chiave di Java lasciare uno spazio bianco, ad esempio:
while (condizione) { ... }
Non utilizzare uno spazio bianco tra il nome del metodo e l’apertura delle parentisi. Questo consente una immediata identificazione del metodo.
Inserire lo spazio bianco dopo la virgola dei paramteri del metodo.
public double getSales(int a, int b, String c) { ... }
Tutti gli operatori (ad eccezione del “.”) devono essere separati da uno spazio bianco:
int y -= x + z; y = (g + j) / (u - o + d);
Dopo che si effetua un cast, si lascia uno spazio bianco.
public double getSales((Object) c) { ... }
Mantenere la leggibilità di eventuali linee di codice lunghe.
Nel caso di linee di codice lunghe e poco leggibili, è un bene, con il fine di aumentarne la leggibilità, spezzare la linea in due o più linee. Ad esempio dopo la virgola puoi andare a capo, oppure dopo un operatore. Considera infine che spezzare il codice in più linee è meglio farlo con le parti di codice che abbiano un livello di parentesi più “alto”.
Inoltre, una volta scritto il codice su più linee, allinea le linee stesse affinchè siano “allineate”, ovvero affinchè le linee inizino verticalmente nello stesso punto.
Ovvero, questo codice è preferibile
keyboard = keyboardTypeA * (keyboardTypeB + keyboardTypeC - keyboardTypeD) + (keyboardTypeU + keyboardTypeI - keyboardTypeL);
a questo codice:
keyboard = keyboardTypeA * (keyboardTypeB + keyboardTypeC - keyboardTypeD) + (keyboardTypeU + keyboardTypeI - keyboardTypeL;
Convenzione dei nomi.
Per i packages:
- adottare la convenzione java per il dominio (ad esempio i packages devono iniziare con it.nomeazienda. ecc.)
- devono essere lowercase
Per le classi:
- devono avere la prima lettera maiuscola
- la classe pubblica di ciascun file deve corrispondere al nome del file
Per le costanti:
- tutti i caratteri delle costanti devono essere maiuscoli
- le parole delle costanti sono unite tramite underscore
public final int UNA_COSTANTE = 10;
Se sono private o protected scriverle con un underscore iniziale poi l’uppercase per il carattere iniziale di ogni parola.
private int _unaVariabile = 3;
Per le variabili locali scrivere il primo carattere lowercase ma le altre parole con il primo carattere maiuscolo
private int unAltraVariabile = 5;
Per i metodi scrivere il primo carattere lowercase ma le altre parole con il primo carattere maiuscolo
public double getSomeFruit() { ... }
Utilizza il new o il create per i metodi del design pattern factory.
Utilizza il to (ad esempio il toNomeOggetto) per i metodi che converto in un oggetto.
Utizza il get e il set per quei metodi che danno o settano un oggetto
AnObject getAnObject(){...} void setAnObject(AnObject){...}
Consigli.
Scrivere un main in una classe separata che utilizza la tua classe applicativa, quindi non mettere il main nella classe applicativa. Il nome della classe del main dovrebbe essere il nome della classe che si luove utilizzare con il postfisso Main, ad esempio MyClass la classe da utilizzare, e MyClassMain la classe che contiene il main.
Scrivere uno Unit test che utilizza la tua classe applicativa.
Nei tests devono esserci esempi di utilizzo delle classi, oltre che a testare le funzionalità, sarebbe auspicabile che ci siano degli esempi di utilizzo.
Non scrivere codice hard coding.
Scrivi degli esempi di utilizzo di classi, preferibilmente nelle classi di tests.
Razionalizza e semplifica.
Se nel design si pensa ad una astrazione della classe allora definire una interfaccia, scrivere invece una classe astratta quando un metodo può essere utilizzato da tutte le sottoclassi.
Le interfacce sono più flessibili delle classi, poichè le classi non supportano l’ereditarietà multipla.
Utilizza i Generics per il tuo codice, ti consente di avere codice più robusto e con meno bugs.
Come gestire le eccezioni.
Quando scrivi il blocco del catch scrivi anche un log con un messaggio oppure stampa lo stack trace.
Scrivere un messaggio significativo dell’evento, non utilizzare generiche espressioni.
Sii dettagliato nel log, ad esempio scrivendo l’id dell’item che va in eccezione, affichè si possa capire successivamente il caso d’uso.
try { ... } catch(Exception e) { e.printStackTrace(); }
try { ... } catch (TransactionException te) { logger.log(te.printStackTrace()); }
A seguire un buon esempio di gestione delle eccezioni.
try { f = new File(path); } catch (IOexception ioE) { String m = ClassLoader.message(FILE_NOT_FOUND, path) throw new FileException(ie, m); } catch (CustomException cE) { logger.log(cE.getMessage()); }
La gestione delle eccezioni devono essere in tutti i metodi in cui c’è la possibilità in cui una eccezione si possa verificare.
Variabili.
Non dichiarare le variabili public se non le dichiari final static.
Questo perchè nell’ Object Oriented Programming il dichiarare una variabile publica “modifica” il modello ad oggetti, in questo caso sposta il potere decisionale al di fuori dell’oggetto, al di fuori della classe.
Inizializza sempre le variabili.
Scrivi minor static possibili, ad eccezione del caso in cui si necessiti di costanti static. Questo perchè le variabili static non sono OO (Object Oriented).
Inoltre si creano anche metodi più dipendenti dal contesto e con eventuali effetti collaterali, con la possibilità di avere problemi sulla sincronizzazione.
Utilizza final per le variabili il cui valore non deve mai essere cambiato.
Quando scrivi le variabili di istanza e metodi considera il principio della minima visibilità, ovvero variabili e metodi devono essere meno esposti possibili alle altre classi e package.
Per le variabili di istanza che necessitano di essere scritte al di fuori della classe utilizzare un metodo con il set iniziale, ad esempio setNomeVariabile.
Non scrivere lo stesso nome di variabile per una superclasse e una sottoclasse, questo è un errore.
Dichiara una nuova variabile locale invece di utilizzarne un’altra che è stata concepita con un altro scopo.
Dichiarare una variabile locale non all’inizio del metodo solo per i cicli.
Assegna null a qualsiasi array, lista, collezione di oggetti che per un lungo tempo non viene utilizzata (Ref. Garbage Collection).
Metodi.
Se possibile non scrivere troppi metodi in successione:
object.methodA().methodB().methodC().methodD();
poichè sia si potrebbe avere uno stato dell’oggetto che non è quello voluto, sia potrebbero esserci problemi di sincronizzazione.
Commenta le classi e i metodi thread safe, è molto utile capirne lo stato e la logica.
Sincronizza metodi piuttosto che blocchi, utilizza syncronized sui metodi piuttosto che nei blocchi, questo consente di aver un incapsulamento migliore.
Se fai l’override di hashcode allora fai anche l’override di equals.
Se fai l’override di equals allora fai anche l’override di hashcode.
A seguire i principali vantaggi del Coding Standard:
- Ottimo quando si amministra e si automatizza il processo di build
- Quando si ha integrazione di codice si ha un codice “collettivo” con lo stesso standard
- Il codice è più facile da mantenere
- Permette una miglior lettura del codice agli altri ma non solo a loro
- Si ha lo stesso formato del codice sorgente e dei commenti, questo consente anche una miglior comprensione da parte di tutti gli sviluppatori del team
- Facilita la comunicazione
Alessandro Ceseno
PS: se hai domande scrivimi pure per mezzo della sezione contatti.