risorse | clean code

Clean Code

A Handbook of Agile Software Craftsmanship

Una raccolta di alcuni interessanti spunti trovati nel libro, raggruppati per capitolo.

1. Clean Code

Il primo capitolo contiene la definizione di clean code secondo l'esperienza dei maggiori esperti del settore; alcune di esse sono riportate qui di seguito:

Si argomenta sul paradosso che il codice sporco, risultato di un'attività frenetica di sviluppo volta ad accelarare i tempi di consegna, si rivela essere in realtà una – se non la principale – causa di ritardo. Viene quindi presentata la metafora della finestra rotta, la cui morale dovrebbe spronare a ripulire il codice ogni qualvolta se ne presenti la possibilità, in particolare durante le fasi di refactoring: anche la ripulitura del codice quindi, può essere effettuata in maniera incrementale.

Viene infine proposta la regola del boyscout adattata al contesto del libro: lascia il codice più pulito di come l'hai trovato.

2. Meaningful names

I consigli proposti in questo capitolo sono piuttosto noti, e spesso si ritrovano in pubblicazioni analoghe (cfr. Pescio, Manuale di Stile C++, Fowler, Refactoring, …):

3. Functions

Anche le indicazioni riportate in questo capitolo, riguardante la scrittura delle funzioni, si trovano spesso citati in pubblicazioni simili; interessante l'idea della stratificazione delle funzioni in analogia con i livelli di astrazione delle operazioni effettuate.

4. Comments

Le avvertenze sono le solite:

5. Formatting

Anche in questo caso non ci si discosta da ciò che si trova in letteratura:

6. Objects and Data Structures

Il sesto capitolo affronta la dicotomia oggetti/strutture, affermando che se l'interfaccia dei primi deve astrarre dall'implementazione, per le seconde ciò non vale, essendo dei semplici contenitori di dati. Seguono due considerazioni:

La tesi viene suffragata considerando che l'introduzione di una nuova entità all'interno di una gerarchia di classi si ottiene derivando la nuova entità da una esistente ed implementandone le funzionalità specifiche; nel caso di una gerarchia di strutture, è necessario intervenire su tutte le funzioni operanti sulle strutture esistenti, dovendo esse ora trattare anche la nuova casistica. Viceversa, l'introduzione di una nuova funzionalità in una gerarchia di classi spesso si traduce nell'aggiornamento dell'interfaccia di gran parte delle classi esistenti; nel caso di strutture, normalmente è sufficiente implementare la nuova funzionalità in un metodo globale.

Limitatamente al dominio degli oggetti, viene citata la legge di Demeter, e viene proposto un metodo per soddisfarla, che consiste nell'esporre parte della funzionalità del contenuto nell'interfaccia del contenitore:

   final String outputDir = ctxt.options.scratchDir.absolutePath;
   ...
   String outFile = outputDir + "/" + className.replace('.', '/') + ".class";
   FileOutputStream fout = new FileOutputStream(outFile);
   BufferedOutputStream bos = new BufferedOutputStream(fout);

si può rendere meglio così:

   BufferedOutputStream bos = ctxt.createScratchFileStream(classFileName);

7. Error Handling

8. Boundaries

L'ottavo capitolo è dedicato alla gestione dei confini del proprio codice, ovvero come rendere il codice di nostra competenza robusto rispetto alla variabilità e alla disponibilità di librerie di terze parti o di codice sviluppato da altri colleghi. Il suggerimento principale è il ricorso al wrapping.

Librerie di terze parti:

Relativamente al codice sviluppato da altri colleghi, qualora questo non sia ancora disponibile, si raccomanda l'uso di mock/stubs per emulare le funzionalità necessarie; questi potranno in seguito essere riutilizzati come adapter dei componenti reali, quando diventeranno disponibili.

9. Unit Test

Il capitolo inizia enunciando le tre leggi del TDD:

  1. non si scrive codice di produzione finché un test non fallisce;
  2. non si scrive codice di test se non quello necessario al fallimento (un errore di compilazione è già di per se un fallimento);
  3. non si scrive codice di produzione se non quello necessario per far passare il test che fallisce.

Il TDD costringe lo sviluppatore a passare continuamente dallo scrivere codice di test – che fallisce – alla scrittura di codice di produzione – che fa passare il test. Il codice progredisce così per piccoli passi, essendo la durata tipica di un ciclo di qualche minuto. Risulta perciò quasi impossibile perdersi nel codice dato che, per definizione, se a un dato momento il test non passa la causa è da ricercare nelle modifiche introdotte nell'ultima iterazione. Ciò regala una notevole serenità al programmatore, che ha la certezza che, qualunque cosa sia successa, pochi undo lo ricondurranno ad una situazione stabile.

Il codice di test va trattato come il codice di produzione. Cosa fa invece di un test case un buon test case?

La copertura del codice di produzione deve essere totale: deve essere testato tutto ciò che in linea di principio potrebbe fallire; non si devono trascurare nemmeno le funzionalità più banali (se non altro, è documentazione).

10. Classes

Oggetto di questo capitolo sono le classi; vengono rispolverati i più importanti principi della progettazione object-oriented:

Non ci si deve spaventare di fronte ad una moltitudine di piccole (super-focalizzate) classi rispetto a poche, vaste classi: essendo le funzionalità implementate le medesime, medesima è la quantità di concetti da gestire e comprendere.

Una classe deve essere coesa:

Un'osservazione interessante è la seguente: favorire la coesione porta all'aumento del numero di classi.

11. Systems

Il capitolo è molto java-oriented, essendo particolarmente focalizzato sull'AOP.

Alcune osservazioni di carattere generale sono dedicate all'inizializzazione del sistema software, inteso come istanziazione degli oggetti principali. Vengono presentate tre differenti tecniche di start-up del sistema software:

12. Emergence

Vengono descritte e commentate le regole del simple design, riportate qui sotto nella loro forma originale:

  1. runs all the tests;
  2. no duplication;
  3. express developer intent;
  4. minimizes the number of classes and methods.

Una volta ottemperato alla prima regola, il refactoring consente di realizzare le successive.

Riferimenti

Martin, Robert C. Clean Code: A Handbook of Agile Software Craftsmanship. Prentice Hall, 2008.

Pagina modificata l'8/11/2011