MokaByte Numero 33  -  Settembre  99
Librerie in Java
strumenti e tecniche






 

di
Antonio Cisternino
In questo articolo e nel prossimo prenderemo in considerazione 
alcune tecniche per sviluppare codice riutilizzabile e per gestire 
grossi progetti in Java
In collaborazione con Computer Programming


Sono molte le novità che Java ha introdotto nel mondo della programmazione. Sicuramente tra le più  importanti troviamo l’imposizione di alcuni standard di codifica e l’esistenza di un tool standard di  documentazione automatica. In questo articolo e nel prossimo illustrerò come sfruttare queste  caratteristiche del linguaggio per organizzare il proprio codice in modo tale che sia riutilizzabile. Le  tecniche che descriverò saranno altrettanto utili per gestire progetti di grandi dimensioni.In questa prima  puntata descriverò innanzitutto gli strumenti disponibili per amministrare il proprio codice e
 successivamente come questi possano essere sfruttati per trarre dei notevoli vantaggi nello sviluppo. La  seconda puntata sarà invece dedicata allo sviluppo di una piccola libreria che faccia uso degli strumenti  illustrati.

Introduzione

Sono molte le novità che Java ha introdotto nel mondo della programmazione. Sicuramente tra le più importanti troviamo l’imposizione di alcuni standard di codifica e l’esistenza di un tool standard di documentazione automatica. In questo articolo e nel prossimo illustrerò come sfruttare queste caratteristiche del linguaggio per organizzare il proprio codice in modo tale che sia riutilizzabile. Le tecniche che descriverò saranno altrettanto utili per gestire progetti di grandi dimensioni.In questa prima puntata descriverò innanzitutto gli strumenti disponibili per amministrare il proprio codice e successivamente come questi possano essere sfruttati per trarre dei notevoli vantaggi nello sviluppo. La seconda puntata sarà invece dedicata allo sviluppo di una piccola libreria che faccia uso degli strumenti illustrati.
 
 
 
 

Gli strumenti offerti dal JDK e dal linguaggio

Iniziamo questa panoramica dagli strumenti offerti dal JDK di Sun. Ho scelto di illustrare le tecniche utilizzando il semplice JDK perché è un tool gratuito. Molte delle considerazioni che farò possono comunque essere utilizzate anche in ambienti di sviluppo che, tra l’altro, spesso si appoggiano al JDK.I tre comandi principali per sviluppare librerie in Java sono: javac, javap e javadoc. Oltre a questi comandi forniti col JDK è possibile utilizzare script di sistema per automatizzare alcuni aspetti della generazione della documentazione e del bytecode.Il javac è il compilatore vero e proprio e consente di compilare le classi contenuti nei file con estensione .java. Questo strumento condiziona già l’organizzazione del proprio codice poiché impone che ogni file contenga al più una classe dichiarata public e che il nome del file coincida col nome di tale classe. Come tutti gli strumenti a riga di comando supporta vari switches che consentono di controllarne il comportamento. In particolare lo switch –d consente di controllare la destinazione dei file .class prodotti dal compilatore.Il comando javadoc consente di produrre la documentazione del proprio codice a partire da commenti speciali inseriti nel sorgente. Il risultato dell’uso di questo strumento è un insieme di pagine HTML che contengono la documentazione. Chi è familiare con la documentazione prodotta da Sun per il JDK ha quotidianamente un esempio di documentazione generata da javadoc.
 
 
 
 

Java e il meccanismo dei package

Una volta introdotti gli strumenti fondamentali consideriamo un meccanismo fondamentale offerto da Java per la produzione di librerie: i package. Col concetto di package Java consente di scrivere codice modulare, contenuto in più file, svincolando però la classe dal file che la contiene. In pratica il sistema Java conosce tutte le classi disponibili poiché contenute in una o più directory del filesystem specificate nella variabile d’ambiente CLASSPATH.Un programma Java è costituito da un insieme di classi. Esse sono a loro volta partizionate in vari package. Un package è quindi un insieme di classi e altri package, ai quali è associato un nome. Un esempio di classi suddivise in package è dato dalla libreria standard di Java.Il nome completo di una classe è costituito dal nome dei package in cui è contenuta separati dal punto; il meccanismo offerto dal costrutto import consente di evitare di specificare il nome esteso di una classe ogni volta che serve.E’ sempre possibile creare nuovi package semplicemente cominciando il proprio file contente il codice Java con il costrutto
 
package it.infomedia;


che nella fattispecie dichiara che il codice contenuto nel file appartiene al package il cui nome è it.infomedia.Cerchiamo di capire come vadano organizzate fisicamente le classi Java perché la Java Virtual Machine possa correttamente trovare i package definiti. Tutto il codice contenuto in un package si deve trovare in una directory che ha lo stesso nome del package. Il punto nel nome di un package serve a definire una gerarchia che si riflette nel filesystem in un insieme di directory.Ad esempio se nel package chiamato it.infomedia c’è la classe DevClass allora il filesystem deve essere organizzato come nella figura seguente 
 
 
  Figura  1

Per costruire un proprio package ci sono due strategie possibili: creare l’albero delle directory e scrivere i sorgenti localizzandoli nel posto opportuno, usare lo switch del compilatore –d. Nel primo caso semplicemente si crea una directory per ogni package (eventualmente annidando le directory) e si crea in ogni directory i file .java relativi al package associato. Sempre seguendo l’esempio in della figura  1 il sorgente DevClass.java contenente la classe DevClass si deve trovare nella directory infomedia. In questo caso per compilare i file di un package è sufficiente un semplice

javac *.java
 

eseguito nella directory infomedia.

Nel secondo caso invece i sorgenti si mettono in una cartella qualsiasi (che può contenere sorgenti di package differenti, anche se è sconsigliabile dal punto di vista organizzativo) e si esegue il comando

javac –d outdir *.java

dove outdir è la directory dove verranno messi i file risultato della compilazione. Il javac inoltre si occupa di creare l’albero delle directory necessario (nella directory outdir) e di depositare i file .class nelle posizioni opportune.Il vantaggio nel secondo approccio è evidente: permette di separare i sorgenti dal codice eseguibile ottenendo una buona organizzazione del proprio codice. Se si sfrutta la naturale gerarchia dei package è possibile racchiudere tutto il proprio codice sotto un enorme package facilitando, come vedremo in seguito, il riuso del codice.
 
 

 

I meccanismi di protezione

I meccanismi di protezione offerti dal linguaggio, mediante l’uso delle keywordpublic, private e protected, consentono di controllare il proprio codice e di definire delle librerie che siano realmente riutilizzabili e facilmente estensibili.Descrivo brevemente il significato di queste parole chiave del linguaggio per rinfrescare le idee a chi non le usa frequentemente. I meccanismi di protezione sono stati introdotti nei linguaggi orientati agli oggetti per permettere un riuso controllato del codice. Il controllo da parte del compilatore dei diritti sui singoli membri di una classe consente, ad esempio, di evitare che una classe utilizzata internamente nella libreria venga usata impropriamente al suo esterno. I modificatori di accesso permettono un maggiore incapsulamento dei dati all’interno della classe impedendo di accedere alle variabili membro della classe se non tramite metodi che ne alterino il valore.La parola chiave public identifica quei membri di una classe che sono accessibili da chiunque. La parola chiave protected identifica quei membri che possono essere acceduti solo nella definizione dei metodi delle classi derivate dalla classe che dichiara il membro protected. La parola chiave private identifica invece i membri che possono essere acceduti solo all’interno della classe stessa. In Java, se non si specifica altrimenti, ogni membro è accessibile solo dalla classe che lo contiene e dalle classi che sono nello stesso package. Questo può essere molto utile per condividere variabili tra classi diverse di uno stesso package senza che queste siano accessibili all’esterno del package stesso.
 
 
 
 
 

Javadoc: documentare automaticamente il proprio codice

Il sistema di generazione automatica del proprio codice è forse la più grande novità offerta da Java come supporto alla codifica di sistemi complessi e librerie. In questo paragrafo descriverò, in maniera compatta, l’uso del tool javadoc per documentare il proprio codice. Successivamente considereremo come questo strumento, unito ai precedenti possa fare miracoli nel riutilizzo del codice. l tool javadoc prevede che il codice sia commentato con dei commenti speciali compresi tra /** e */. Ovviamente questi commenti sono un caso particolari dei commenti previsti per il linguaggio e quindi non sono identificati dal compilatore. Sono inoltre previste delle direttive particolari per informare il sistema di documentazione sul significato dei commenti; queste direttive cominciano col carattere @.I commenti javadoc solitamente precedono una classe, oppure uno dei membri della classe; il sistema considera un commento associato al membro che segue immediatamente nel sorgente e lo usa per generare la documentazione relativa ad esso. In questo modo è possibile associare un commento ad ogni membro di una classe e alla classe stessa. I commenti sono espressi in HTML e possono quindi contenere tabelle e ogni sorta di tag HTML (persino delle applet).Per capire meglio come funziona il sistema supponiamo di voler commentare la seguente classe 
 
class foo {
  public static final int CONST_FOO = 0;
  public foo(String str) throws java.io.IOException { }
  public int sqr(int x) {
   return x*x;
  }
}
Un possibile modo per commentare la classe è riportato nel seguente listato 
**
 * Commento della classe <b>qui</b>.
 * @version 1.0
 * @author Antonio Cisternino
 */
class foo {
  /**
   * Commento sulla costante
   */
  public static final int CONST_FOO = 0;
  /**
   * Commento per il costruttore
   * @param str Commento relativo al parametro
   * @exception java.io.IOException Commento relativo all'eccezione
   */
  public foo(String str) throws java.io.IOException {
  }
  /**
   * Questo &egrave; un metodo che calcola la radice quadrata di
   * un numero intero.
   * @param x Numero da elevare al quadrato
   * @return Il quadrato dell'argomento passato come parametro
   */
  public int sqr(int x) {
    return x*x;
  }
}


La maggior parte delle direttive del sistema di documentazione automatica si spiegano da sole e sono riportate in questo esempio. Osservate come gli asterischi usati per indentare i commenti vengano ignorati dal javadoc. Inoltre ho usato di proposito tag HTML e lettere accentate nello stile HTML per sottolineare che questo è il linguaggio generato dal sistema.Una volta commentati i propri sorgenti è sufficiente invocare il javadoc (nello stile del javac) per ottenere un insieme di pagine HTML contenenti la documentazione del proprio codice nello stile della documentazione delle API del JDK.Dalla versione 1.2 del linguaggio è stata introdotta una nuova tecnologia chiamata doclets che verrà in parte descritta nella prossima puntata e che permette di controllare l’output del sistema di documentazione per caratterizzare il formato grafico della documentazione.
 
 

 

Come usare gli strumenti descritti?

Siamo finalmente giunti al momento in cui posso descrivere come utilizzare gli strumenti finora descritti per realizzare librerie e codice realmente riutilizzabile.
Di seguito descriverò alcune tecniche (forse è meglio chiamarle considerazioni utili) di base. Nella prossima puntata mi concentrerò invece su feature avanzate come la gestione delle risorse in una libreria, l’internazionalizzazione, l’uso dei file jar e la personalizzazione della documentazione come strumenti di livello più elevato che consentono lo sviluppo di librerie ancora più versatili.
Non voglio entrare nei dettagli metodologici che si nascondono dietro la strutturazione del codice di un grosso progetto o di una libreria poiché sarebbe argomento troppo spinoso e complesso per essere affrontato in un articolo. Piuttosto vorrei soffermarmi a riflettere cosa possono fare questi strumenti per aiutare a strutturare e mantenere il proprio codice.
Una delle principali novità introdotte con Java e legate al meccanismo dei package è che non esiste più il concetto di file eseguibile bensì l’insieme di classi Java compilate e suddivise in package. Grazie al meccanismo gerarchico dei package è possibile raccogliere tutto il proprio codice sono un grosso package che poi si differenzia prima per progetto, poi per modulo e per funzionalità. Sun suggerisce di usare il meccanismo gerarchico tipico delle URL al contrario. Il codice sviluppato nell’ambito di Dev, ad esempio, potrebbe trovarsi nei package che hanno come prefisso it.infomedia.dev.
Strutturare il proprio lavoro in package risulta quindi una scelta vincente dal punto di vista del riuso del codice poiché nell’ambiente Java sarà sempre disponibile tutto il proprio lavoro. Ovviamente è importante individuare come suddividere le proprie classi per far sì che sia sempre possibile individuare il numero di package adeguato per non regalare più di quanto promesso ad un committente.Una strategia che spesso io uso per organizzare il mio personale albero di codice Java è quello di creare un package per ogni progetto, se necessario spezzare ulteriormente il package in sotto-package. Se durante lo sviluppo ci si accorge che si stanno sviluppando classi che potrebbero essere utili in futuro si devono adottare due accorgimenti: creare un package (o spostare la classe in un package già esistente) dove mettere la classe con quelle che immediatamente o in futuro saranno correlate; cercare di meditare con attenzione sulle interfacce e sui controllo di accesso alla classe per garantire la massima riusabilità.In Figura 2 è riportato un esempio di albero di package che ho adottato nello sviluppo del sistema CompAss presso l’Università di Pisa. 

  Figura  2

 

Come si può notare il package compass è affiancato da molti package che sono stati sviluppati nell’ambito di questo progetto ma che ho successivamente riutilizzato. Esempi di questo tipo di package sono il package vserver che incapsula un server TCP concorrente e il package jcx che contiene un certo numero di componenti grafici che possono essere riutilizzati in altri progetti. Osservate infine come tutto l’albero dei package sia sotto it.unipi.di che è il prefisso dei package sviluppati presso il dipartimento di Informatica dell’Università di Pisa.La documentazione del proprio codice è sicuramente fondamentale per la manutenzione del codice. Sviluppando con Java ho imparato certamente una cosa: l’uso di javadoc è essenziale per mantenere la sincronia tra ciò che fa il codice e ciò che è scritto nella documentazione. Per fare un esempio non banale, il già citato sistema CompAss fa uso di un certo numero di linguaggi per cui è stato necessario specificare la sintassi. L’unica ragione per cui la sintassi implementata è coerente con quella nella dichiarazione è proprio che ho potuto aggiornare la documentazione direttamente nel sorgente ad ogni modifica. Inoltre tutte le librerie sviluppate, essendo ben documentate, sono state riutilizzate in altri progetti, anche non da me che le avevo sviluppate.
 
 

 

Conclusioni

In questo articolo ho presentato gli strumenti di base che Java offre per gestire il proprio codice. Nella prossima puntata invertirò le parti e mi concentrerò sullo sviluppo di librerie introducendo occasionalmente feature avanzate quando necessario. Svilupperò inoltre una piccola libreria come esempio pratico di applicazione delle tecniche descritte.

  
 

MokaByte rivista web su Java

MokaByte ricerca nuovi collaboratori
Chi volesse mettersi in contatto con noi può farlo scrivendo a mokainfo@mokabyte.it