MokaByte
Numero 34 - Ottobre 99
|
||||
|
|
II parte, un esempio |
||
|
Antonio Cisternino |
|
||
|
||||
L’articolo
precedente ha introdotto gli strumenti offerti dal linguaggio Java per
documentare e riutilizzare il proprio codice. In questa seconda ed ultima
puntata scriveremo una piccola libreria di esempio capire in concreto quali
spunti possano, con poco sforzo, portare grandi benefici nell’economia
dello sviluppo del software in Java.La gestione presentata non richiede
altro che il java development kit. L’uso di strumenti per gestire i progetti
non impedisce di impiegare le stesse tecniche qui presentate, si tratterà
solo di capire come un ambiente di sviluppo consenta di organizzare il
proprio codice secondo lo schema presentato.
|
Il problema Per sviluppare una libreria innanzitutto si deve avere chiaro in mente il problema che si vuole risolvere. Se non si vuole sviluppare middleware, spesso la libreria è un sottoprodotto di un software che si scrive per altre ragioni; in questo caso il problema non è lo sviluppo della libreria ma quello di un software. Spesso però mentre si sviluppa il software ci si rende conto che una sua parte risolve un problema sufficientemente generico, e per tale ragione tale porzione debba essere sviluppata come libreria.Ecco quindi che si ha il problema di disegnare un modulo software che offra delle interfacce sufficientemente generiche da essere riutilizzate il maggior numero di volte possibile. Successivamente può capitare che ci si accorga che con poco sforzo si può risolvere un numero di problemi maggiore, magari offrendo una chiamata in più o aggiungendo un parametro in più. Quello che si deve valutare è lo sforzo necessario alla generalizzazione: se si dovesse rivelare troppo grande allora è meglio produrre una libreria più mirata.Capita spesso inoltre che a generalizzare troppo non si risolva più il problema poiché sono troppi i parametri richiesti e la complessità richiesta nell’uso della libreria è equivalente alla riscrittura del modulo stesso.Quando si sviluppa una libreria si ha un ben preciso problema: definire l’interfaccia e l’implementazione di un modulo per uno sviluppatore che lo integrerà nel proprio software. Una volta compresa la natura del problema in generale può capitare di dover scegliere il linguaggio di programmazione e le tecnologie in gioco, ma non è il nostro caso poiché stiamo parlando di librerie scritte in Java.Nel caso in cui una libreria nasca durante lo sviluppo di un software, accade altrettanto spesso che sia in realtà un pezzo di codice già scritto che viene confinato in un modulo chiamato libreria. In questo caso spesso non sarà possibile riutilizzare quel codice poiché le interfacce che offrirà la nuova libreria così creata saranno tipicamente molto vincolate al software stesso, riducendo quindi l’applicabilità in altri ambiti.Il problema che affronterò in questo articolo è quello di scrivere una libreria che astragga il concetto di canale di log. Quante volte capita di dover generare messaggi che descrivono l’andamento dell’esecuzione del programma? Accade veramente spesso anche se il mezzo di output è spesso e volentieri differente. Inoltre i programmi in genere permettono di specificare il grado di dettaglio del log da generare etichettando in qualche modo i messaggi offerti e selezionando in base alle etichette abilitate.In questo articolo verrà presentato lo sviluppo di una libreria che gestisca i canali di log consentendo di essere estesa facilmente per aggiungere nuove tipologie di canale. Il risultato sarà una piccola libreria contenuta nel package it.mokabyte.logmanager che permette di includere un supporto non banale della gestione del log nelle proprie applicazioni.
Il LogManager Quando mi sono trovato ad affrontare il problema del log mi sono posto i seguenti quesiti: dove viene inviato il messaggio? Come è possibile consentire la selezione dell’output?Queste domande, anche se in apparenza banali, hanno originato un’interfaccia Java che io ho chiamato LogManager che astrae il concetto di canale di log. I metodi offerti da questa interfaccia riflettono la mia volontà di dare una risposta alle precedenti domande in modo parametrico. I metodi offerti dall’interfaccia sonoSi veda il Listato 1 per la definizione completa: Listato 1 L’interfaccia LogManager Il metodo fondamentale
è il primo che consente di aggiungere al canale di log un
messaggio di un dato tipo. Il tipo di un messaggio è un’informazione
che classifica i messaggi e che successivamente permetterà un opportuno
filtro per classi di messaggi.Il metodo setClassLog è quello
che risponde all’esigenza di filtrare: permette infatti di abilitare o
disabilitare tutti i messaggi etichettati con un certo tipo.Infine il metodo
isSelective permette di sapere se l’implementazione dell’interfaccia
supporta il log selettivo o meno. L’esistenza di questo metodo è
dovuta alla volontà di rendere disponibile l’interfaccia anche per
gli implementatori che vogliono solo astrarre il canale ma non offrire
alcun tipo di filtro.L’interfaccia definisce anche le classi di log,
ovvero le etichette utilizzabili per caratterizzare il log: sono otto interi
scelti in modo da avere un solo bit a 1 per poi poter utilizzare gli operatori
booleani per abilitare più classi con un solo comando. Ad esempio
il comando:
setClassLog(CLASS[1] & CLASS[3], true)
Il GenericLogManager Nel paragrafo
precedente ho mostrato l’interfaccia attorno a cui si costruisce il concetto
di canale di log; una libreria però normalmente deve anche
offrire qualcosa in più di un’interfaccia. Tipicamente deve offrire
l’implementazione dell’interfaccia per casi utili come ad esempio l’output
del log su console.Nel nostro caso sarebbe anche utile fornire una
gestione di default del sistema di filtro in modo tale che l’unica cosa
che resta successivamente da implementare è la stampa dei messaggi
di log sul canale appropriato. Ecco quindi che ho deciso di definire
una classe base astratta che fornisca già una possibile implemetazione
del meccanismo di filtro dei messaggi e che chiami un metodo print
che stampa un messaggio sul canale. Ecco quindi che per ottenere un canale
nuovo è sufficiente derivare una classe che implementa il metodo
in questione per ottenere un nuovo gestore del canale di log.Il
ConsoleLogManager è definito come segue:
public class ConsoleLogManager extends GenericLogManager {
txt.appendText(str + "\n");
Listato 2 La classe GenericLogManager Un’ultima cosa da osservare è la possibilità di inserire un time stamp nel messaggio utilizzando un segnaposto $$ nel messaggio di log.
Non bastava il GenericLogManager? La domanda può
apparire legittima. Può sembrare che la classe GenericLogManager
sia sufficientemente generale da rendere inutile l’interfaccia LogManager
da cui eravamo partiti. In realtà non è così: si supponga
di voler realizzare un canale di Log che stampi su due canali differenti.
Ci sono molte applicazioni di questa struttura: potrebbe essere infatti
necessario filtrare i messaggi sullo standard output ma immagazzinare
l’intero log su un file. Ovviamente con un notevole sforzo può
essere fatto utilizzando la classe descritta nel paragrafo precedente.
D’altra parte implementando direttamente l’interfaccia LogManager
è possibile scrivere la classe LogManagerPair riportata nel
Listato 3 e che funziona come replicatore di messaggi: dato un messaggio
in input al canale lo replica su due canali di log precedentemente definiti.
Listato 3 La classe LogManagerPair
Tornando alle librerie… Dopo aver raccontato la genesi e lo sviluppo di una mini libreria è bene soffermarsi a riflettere su ciò che si è fatto e come questo sia legato a quello che voglio descrivere. La libreria appena descritta è nata da un problema comune in cui la possibilità di riutilizzare il codice consente un risparmio nel tempo di sviluppo e quindi una maggiore produttività. È questa caratteristica che fa si che si possa parlare di libreria piuttosto che modulo in un sistema software.Una volta definito il problema si è cercato di dare una soluzione soddisfacente che con un costo relativamente ridotto permettesse di ottenere un modulo realmente riutilizzabile. Inoltre siamo riusciti ad ottenere una libreria facilmente estendibile, come si è potuto osservare dall’esempio del ConsoleLogManager.Ci possiamo chiedere ora: si poteva fare meglio? La risposta è ovviamente affermativa: si poteva, ad esempio, sfruttare il concetto di stream offerto da Java per definire il canale ottenendo una libreria più integrata con la libreria standard. D’altra parte probabilmente il gioco non sarebbe valso la candela poiché lo sforzo concettuale per definire le interfacce sarebbe stato sicuramente superiore.Fatta questa premessa veniamo ai tool descritti nel capitolo precedente e a come possono essere utilizzati per impacchettare la libreria e renderla distribuibile.Per compilare la libreria è sufficiente, come abbiamo visto nella puntata precedente, il comando javac –d . *.javaeseguito nella directory contenente il codice. Il risultato della compilazione è la directory it contenuta nella stessa cartella contenente la directory mokabyte all’interno della quale si trova logmanager che contiene le classi compilate. Se si fosse specificata un’altra destinazione al posto di ‘.’ avremmo potuto generare il codice direttamente in un repository di classi sempre presente nel CLASSPATH.Ecco quindi che la nostra libreria è pronta per essere utilizzata nelle nostre applicazioni. Cosa fare ora per distribuirla? Ovviamente bisogna preparare la documentazione; ma io durante lo sviluppo del codice sono stato accorto ed ho annotato tutti i metodi e i membri delle classi con i commenti compatibili con javadoc. È quindi possibile generare automaticamente generare la documentazione della libreria. Osservare nel codice riportato nei riquadri come il commento sia in formato javadoc e utilizzi i marcatori speciali ‘@’ per definire a cosa sono associati i commenti. Inoltre la parola chiave @see consente di specificare un riferimento ad una classe che il generatore di documentazione traduce in un link alla documentazione della classe specificata. Verso nuovi orizzonti Una volta vista
la nascita di una libreria dovrebbe essere semplice produrre le proprie
librerie ottenendo in poco tempo un notevole repository di codice
usabile con successo nei propri progetti. Quali elementi però mancano
in questo panorama? Quali tecniche sono disponibili e non sono state ancora
utilizzate?L’organizzazione in package del proprio codice è
un passo necessario per costruire librerie; un’altra necessità che
si presenta spesso è quella di accedere a delle risorse distribuite
insieme alla libreria.Per mostrare queste capacità in più
assumerò di sviluppare una libreria che contiene componenti grafici
riutilizzabili. In particolare la libreria it.mokabyte.gx conterrà
una comodissima message box come quella fornita con il package
swing contenuto nella versione 1.2 del JDK. Tralasciando la
parte di sviluppo grafico, vorremmo poter distribuire insieme alla libreria
anche le icone (immagini) da visualizzare accanto al testo e rendere parametrica
in base alla lingua il titolo della message box.Per poter includere
risorse nelle proprie librerie è possibile a partire dalla versione
1.1 di Java utilizzare i metodi getResource e getResourceAsStream
forniti dalla classe Class. Sfruttando questi due metodi è
possibile localizzare le immagini direttamente nelle directory contenenti
i file .class generati dal compilatore. Inoltre è possibile
utilizzare la classe ResourceBundle per poter caricare le risorse
dipendenti dalla località in cui viene eseguito il codice. In particolare
la classe PropertyResourceBundle consente di ottenere risorse dipendenti
dalla località persistenti e quindi caricate da uno stream magari
ottenuto con il metodo getResourceAsStream.Sfruttando questi strumenti
efficaci è quindi possibile ottenere la funzionalità essenziale
di una libreria di poter accedere a risorse esterne.
Conclusioni In questo articolo
ho descritto brevemente lo sviluppo di una libreria per gestire il log
di un’applicazione, cercando di mostrare come scelte progettuali ne condizionino
il disegno e quali siano i passi da seguire. Successivamente ho discusso
come utilizzare gli strumenti offerti dal JDK per impacchettare
la libreria ed infine sono stati descritti alcuni strumenti che non erano
stati utilizzati ma che spesso risultano essenziali.
|
|
||
|
||
MokaByte ricerca
nuovi collaboratori
|
||
|