Introduzione
La
documentazione ufficiale fornita da SUN è molto migliorata nel JDK1.2
ma, comunque, si presenta come un reference e non come un manuale d’uso.
Con questa rubrica, quindi, non ci poniamo scopi di completezza o super-approfondimento
(per quanto contiamo di dare qualche dritta dove capita); vogliamo,
piuttosto, tracciare una mappa che consenta di navigare nell'oceano di
classi e trovare rapidamente ciò che meglio si adatta ai nostri
bisogni, che siano di calcolo, di comunicazione o di sicurezza, che richiedano
di macinare stringhe o file, immagini o animazioni, compressioni
o encriptaggi e, senza mettere limiti alla provvidenza, provare a scendere
anche nei meandri dei domini di uso meno frequente e delle estensioni standard.
Questo
primo articolo, prima di entrare nel vivo del package Java più
usato, java.lang, presenta una breve panoramica sull’uso delle librerie
nei moderni linguaggi di programmazione. Si tratta di argomenti noti
a molti ma che potrebbero aiutare qualche neofita di Java, magari già
esperto di altri linguaggi come BASIC e C, ad inquadrare meglio l’argomento
di questa nuova serie.
Dalla
funzione al componente: linguaggi General-Purpose e Librerie di Funzioni
Inizialmente
i linguaggi di terza generazione (FORTRAN, COBOL, BASIC, Pascal, etc…)
non avevano una chiara linea di demarcazione tra linguaggio ed operazioni
su specifici tipi di dati: tutto veniva fornito in un unico calderone sotto
forma di parole del linguaggio; così nessuna distinzione era fatta
tra print,
shell, if, mid$, etc…
Questo
brodo primordiale si rivelò subito poco maneggievole e, ben
presto, vennero fatte distinzioni tra le strutture del linguaggio (if,
for, while etc…), dati di base (p.e. integer,
string,
float)
e dati ed operazioni specifici di determinati domini (records, tipi utente,
funzioni inerenti un certo tipo di dato).
In
particolare il C Language modificò il modo di pensare dei
programmatori che subito capirono la comodità di avere un linguaggio
minimalista,
che implementasse solo le strutture ed i paradigmi di base (puntatori,
array, funzioni, regole di visibilità, dichiarazioni di tipo, etc…)
ed i tipi di dati macchina, ovvero corrispondenti a quelli supportati
direttamente dall’hardware (interi, floating point, caratteri ed indirizzi).
Si diffuse così il concetto di Function Library (in inglese
biblioteca
ma ormai universalmente tradotto come libreria di funzioni): con
qualche difficoltà iniziale dei programmatori BASIC o Pascal, bisognava
decidere i domini dei dato di interesse per ogni applicazione, includere
gli specifici file di definizione (headers) e linkare le
specifiche librerie.
Le
funzioni venivano catalogate in modo che il programmatore non fosse costretto
a linkare troppe funzioni non usate ma, al tempo stesso, non dovesse muoversi
nella frammentazione di migliaia di librerie (solo in un secondo tempo
i linker sarebbero diventati abbastanza intelligienti da scartare
le funzioni non utilizzate). I criteri adottati per tale categorizzazione
erano fondamentalmente due:
Frequenza
d’uso
Dominio
comune di dato
Venne
individuato un insieme di operazioni, per quanto non incluse nel linguaggio,
praticamente utilizzate in tutti i programmi: tali funzioni furono raggruppate
in librerie standard: in particolare stdlib e stdio
(per il C) fornivano da sole una potenza di fuoco quasi pari a quella
dei linguaggi di tipo BASIC.
Le
altre librerie raccoglievano operazioni accumunate dall’avere per oggetto
lo stesso tipo di dato (o un piccolo insieme di tipi correlati). Esempi
di questo tipo sono math,
string, file. A pensarci
adesso, fu anche merito di questo tipo di raggruppamento se si fece largo,
al di fuori degli ambienti accademici, l’idea di Oggetto, o megglio
di Classe, intesa come definizione di tipo di dato comprensivo dell’insieme
di operazioni (metodi) consentiti su di esso.
Classi,
Package e Framework
Tutti
i linguaggi ad oggetti sono, più o meno consciamente (per molti
sarà un’eresia, ma tant’è), discendenti dalle filosofia dei
moderni linguaggi General-Purpose. Ciò che intendo dire è
che essi implementano i soli meccanismi di base, i tipi di dato elementari,
le strutture di controllo e, in modo non dissimile dal buon vecchio linguaggio
C, demandano le operazioni effettive a specifiche librerie di dominio,
non Function Library, in questo caso, ma Class Library. Non
solo C++, Objective-C o Java, che sono chiari eredi del C anche sotto il
punto di vista sintattico, ma persino Smalltalk o Eifel,
in fondo, hanno alla base questa impostazione.
In
realtà le librerie di classi, partendo da tale base, vanno molto
più in là. Se package Java come Math o classi come
String
possono essere visti come i corrispettivi delle omonime librerie C, altre,
come Observer ed Observable, costituiscono delle alleaze
allo scopo di implementare determinati patterns di uso frequente
e non sono, quindi, accumunate dei medesimi dati ma, piuttosto, da un scopo
comune. Tralasciando le varie liberie (STL, MFC, etc…) del C++ e limitandoci
a Java, troviamo dei mastodontici esempi del genere in alcuni package
(collezioni
di classi java correlate: utilizzabile come sinonimo di Libreria di Classi).
In particolare AWT e Swing: ci riferiamo a librerie di questo genere come
framework.
Mentre
le comuni Class library possono essere considerate come tools disponibili
al bisogno per fare determinati lavori durante una elaborazione il cui
corso viene definito dal programma che ne fa uso, i framework hanno un’impostazione
esattamente opposta.
Quando
creiamo, con Swing, una finestra con un menù, non scriviamo una
riga di codice per programmare il loro comportamento; al massimo
riscriviamo un metodo virtuale già esistente per modificare alcuni
comportamenti standard predefiniti (tipicamente le azioni corrispondenti
alle varie voci di menù). Il nostro approccio diviene, in buona
parte, dichiarativo:
Definisco
un’applicazione che è una finestra
Definisco
un menù dentro la finestra
Al limite,
non c’è bisogno d’altro. Il programma viene eseguito e la finestra
si comporta come ci si attende debba comportarsi una finestra per bene,
il menù si aprirà sotto al cursore ed eseguirà le
azioni predefinite (normalmente azioni nulle). La differenza tra un framework
ed una comune libreria di classi (per quanto entrambi siano definiti internamente
a dei package) è quindi la seguente: le semplici librerie sono passive
(implementano comportamenti su richiesta dei programmi) mentre i framework
sono attivi (reagiscono in modo generico agli eventi esterni e si aspettano
che i programmi si configurino come specializzazioni del comportamento
standard da essi fornito).
Componenti,
Collezioni, Object Model e Scripting
Per
concludere, la new wave della programmazione, o se preferiamo, l’estrema
conseguenza della filosofia ad oggetti, è data dal concetto di componente.
Un componente è un insieme di classi che presenta alcune interfacce
standard che consentono a vari linguaggi (anche diversi da quelli utilizzati
per scrivere il componente) e tool visuali di navigare attraverso
i metodi e le proprietà, scoprire i tipi di parametri e le convenzioni
utilizzate e, infine, utilizzarli quando necessario.
Alla
base dei componenti ci sono alcuni meccanismi determinanti. Prima di tutto
quelli di interfaccia e di link dinamico: è importante
che il sistema di sviluppo (p.e. VisualBasic o VisualAge) riconosca alcuni
protocolli comuni con i componenti che utilizza. Ogni componente
viene poi caricato in modo dinamico ed implementa, in modo diverso, i metodi
definiti nel protocollo.
Prendiamo
ad esempio un ipotetico componente per la gestione della stampa: chiamiamolo
printing.
La
sua interfaccia standard di componente (JavaBeans in Java,
IUnknow
per
ActiveX, etc…) consente l’accesso alle proprietà ed ai metodi da
parte del tool di programmazione. Una possibile realizzazione è
la seguente (metodi in verde, proprietà in rosso):
Printing:
printers
(collection
of Printer)
add
count
remove
get
setDefaultPrinter
getDefaultPrinter
Printer:
type
print
newPage
setLoader
setDoubleSide
showConfigurationWindow
Non tutti
i componenti presentano l’intero insieme di interfacce: solo le stampanti
laser consentono la scelta del cassetto di caricamento mentre solo quelle
predisposte per il fronte retro avranno metodi per l’attivazione di tale
opzione.
L’esempio
presenta la tipica struttura di un componente:
un’interfaccia
d’accesso (sottointesa nell’esempio)
uno
o più oggetti radice (in questo caso printing)
alcune
proprietà (p.e. printers) sono definite come collezioni
di altri oggetti (in altre parole fungono da punto di accesso per oggetti
contenuti).
gli
oggetti contenuti (p.e. Printer) possono essere interrogati per la loro
definizione attraverso la medesima interfaccia dei componenti e, a loro
volta, possono contenere altre collezioni di oggetti.
Si tratta,
quindi, di una struttura gerarchica formata da oggetti che contengono collezioni
ad altri oggetti e così via. Questa rappresentazione
classica
ad albero, che viene utilizzata comunemente per documentare la struttura
di un componente, viene detta Object Model.
La
forza di questo paradigma sta nel fatto che grandi applicazioni possono
essere scomposte in un certo numero di componenti che interagiscono tra
loro ed essere, quindi, descritte tramite uno specifico Object Model. In
questo modo è possibile scrivere programmi che automatizzano
il comportamento di altri programmi. A tale scopo si sono affermati linguaggi
semplificati (p.e. senza type checking), detti di Scripting, facilmente
utilizzabili dagli utenti finali (magari appena un po’ evoluti) che permettono
di comporre pezzi di programmi diversi per nuovi scopi. Così, mentre
voi professionisti sgobbate e scavate nei meandri del three tiers, degli
Application Server ed altre amenità, il vostro collega dell’amministrazione
mette insieme, in mezza giornata, un grafico Excel ed una form HTML, scrive
due righe in JavaScript e schiaffa il tutto su una pagina INTRANET che
permette ai vari manager di calcolarsi al volo qualche dato di vitale
importanza, guadagnando il plauso generale e qualche segreto anatema da
parte vostra.
Package
di base
Se
avete programmato in C, sarà diventato un automatismo quello di
cominciare i vostri sorgenti con
#include
<stdio.h>
#include
<stdlib.h>
o,
in ambiente Windows, con
#include
<windows.h>
Ogni
tentativo di scrivere programmi C senza l’inclusione di librerie atte ad
interfacciarsi con il sistema operativo ospite è votato all’insuccesso
(almeno che non siate esperti nel produrre software di base).
Se
invece avete più dimestichezza con il VisualBasic, troverete normale
cominciare a scrivere una subroutine dichiarando variabili di vari tipi,
anche non elementari come collection e control, e immediatamente agire
su di esse tramite i relativi metodi. Se, però, dovete usare componenti
più complessi, come ad esempio un grafico Excel, siete obbligati
ad andare nell’apposita lista dei references per includere le classi necessarie.
Nel fare questa operazione scoprirete di avere alcuni reference già
collegati implicitamente dal sistema, alcuni addirittura obbligatori. Ecco
svelata la magia delle classi standard di VB: sono anch’esse delle
librerie esterne ma collegate per default.
In
Java accade qualcosa di molto simile: il compilatore, javac, provvede
al link automatico del package java.lang. Nulla ci impedisce di
dichiarare l’uso di tale package in modo esplicito, con:
import
java.lang.*;
Anche
se tale dichiarazione viene omessa, Java non può fare a meno delle
classi contenute in tale package in quanto, molte di esse, sono da supporto
ai meccanismi del linguaggio. Basti pensare che, per definizione, tutte
le classi Java derivano da Object che è definita in java.lang.
Per
quanto riguarda la grana delle sue librerie, Java è assai
più modulare di VisualBasic e, per poter disporre di un volume di
funzionalità paragonabile a quello della sola libreria standard
VB, è necessario dichiarare, come minimo, i package java.util,
java.io, java.math etc… Se, inoltre, i nostri programmi devono
disporre di un’interfaccia grafica, sara necessario ricorrere ad uno o
più package, scegliando ad esempio tra java.awt, e java.swing;
già la possibilità di fare questo tipo di scelte rappresenta
un vantaggio rispetto al VB che, invece, deve portarsi appresso forzatamente
un bagaglio standard tutt’altro che leggero.
Proseguendo
con i paragoni, sia Java che VB collegano le proprie librerie in modo dinamico,
potendo scegliere i componenti effettivi da usare anche a run-time. Tuttavia
Java estrae dai suoi package le sole classi effettivamente utilizzate mediante
un meccanismo di indirizzamento basato sui nomi e, fondamentalmente, simile
a quello che consente di rintracciare archivi in un file system a partire
dalla radice; VB, invece, effettua il collegamento con ogni classe in base
ad un ID univoco che deve essere memorizzato nel registry di Windows. Probabilmente
al fine di non sovraccaricare il registy con un’infinità di entry,
la grana dei componenti collegati è solitamente abbastanza grande
(spesso pari a diverse decine di classi) e, di conseguenza, un programma
VB è spesso costretto ad inglobare un gran numero di funzionalità
superflue ai suoi scopi.
Al
termine di questa panoramica, che spero sia stata di qualche utilità
per i Java Beginners prevenienti da altri ambienti di sviluppo,
iniziamo a dare un’acchiata ai tre package che ogni programmatore Java,
indipendentemente dal suo campo di interesse, dovrebbe conoscere in modo
approfondito. |