Caricamento delle classi
L'applicazione ha un'architettura modulare, e
quindi, si desidera che sia possibile estendere le funzionalità
dell'applicazione, ovvero nel nostro caso inserire nuovi giochi. Chiaramente
si vuole che l'inserimento di nuovi giochi non richieda la modifica
del codice esistente, ma bensì il sistema deve essere in grado
in modo automatico di rilevare quali moduli di gioco sono installati.
Questo problema è presente sia a livello di client che di server,
dato che ogni nuovo gioco richiede la creazione di un modulo client
e di un modulo server. Dato che l'ambiente di sviluppo è Java
è naturale pensare che ad ogni modulo corrisponda un file JAR
(Java ARchive).
Si pone quindi il problema di riconoscere e soprattutto
di caricare delle classi da un modulo di gioco, anche se la presenza
di questo modulo potrà essere determinata soltanto a tempo di
esecuzione. Per realizzare questo meccanismo si è fatto uso delle
API della "reflection" di Java, contenute nel package java.lang
e java.lang.reflect , ed in particolare della classe Class ,
che rappresenta una classe o un'interfaccia in un'applicazione Java
in esecuzione. Tale classe è dotata di un metodo che consente
di caricare una classe dato il suo nome:
Class class = Class.forName(String className);
Una volta che la classe è stata caricata,
sarà possibile ottenere un'istanza di tale classe, con il metodo:
Object class.newInstance();
Questo però è possibile
solo nel caso in cui la classe sia dotata di un costruttore
senza argomenti. Altrimenti le cose sono un po' più
complicate ed è necessario ricorrere ad altre classi
della reflection come Constructor . Tuttavia nel
nostro caso è sufficiente usare il metodo appena visto
che le classi che implementano il server e il client dei giochi
saranno dotati di un costruttore senza argomenti.
Perchè il caricamento della classe
abbia successo, è necessario che la classe sia contenuta
nel CLASSPATH della JVM attualmente in esecuzione,
altrimenti la classe non verrà trovata. Visto che nel
nostro caso la classe da caricare si trova all'interno di
un file JAR, per ovviare a tale problema ci sono due soluzioni:
- Si fa in modo che il file JAR sia indicato all'interno del
CLASSPATH
- Anzichè usare
Class.forName() , si usa il metodo
loadClass della classe URLClassLoader , la
quale permette di caricare una classe a partire da un URL (nel nostro
caso l'URL sarà l'indirizzo del file JAR).
Rimane ora il problema di determinare
il nome della classe da caricare, cosa che deve avvenire runtime
visto che a priori non è possibile conoscere i nomi
dei giochi installati. Per fare questo si è adottata
una regola per assegnare i nomi ai vari moduli. Questa regola
consiste nel prendere il nome del gioco (ad esempio Risiko)
e fare in modo che i file JAR abbiano nome formato dal nome
del gioco più la parola "Client" o "Server"
a seconda. Ad esempio, nel caso di Risiko, i due file si chiameranno
RisikoClient.jar e RisikoServer.jar. In questo modo è
possibile, a partire dal nome del file, risalire al nome del
gioco e di conseguenza anche al nome della classe da caricare.
Infatti, anche per i nomi delle classi e delle interfacce
si sono adottate delle apposite regole di denominazione.
Pertanto l'applicazione (sia il client che il
server) quando viene avviata va a scandire un'apposita cartella del
file system locale ed in base alla presenza dei file JAR determina quali
giochi sono installati.
Per il caricamento delle classi, è stata
poi creata una classe NetgameClassLoader , avente soltanto
metodi statici che consente di caricare il client o il server di un
gioco dato il suo nome, con i seguenti metodi:
public static IGameServer loadServer(String
gameName) throws ... ;
public static IGameClient loadClient(String gameName) throws ... ;
|