|
Ens vigilen!
|
|
|
|
|
|
Imagineu que rebem l'encàrrec d'escriure el programari per a
un sistema de control de personal adaptat a les necessitats d'un institut.
L'encàrrec implica que:
- Hem de fer tarjetes per a tots els estudiants que fitxen a
l'entrar i al sortir de l'institut
- Hem d'organitzar l'accés al servidor de l ainformació
perquè els pares puguin supervisar, en línia, els moviments
dels seus fills.
Per a resoldre aquest problema amb senzillesa, necessitem pensar acuradament
en quines peces necessitem.
Aquest és el diagrama que suggerim:
- Una clase Moviment amb la informació
necessària per a cada entrada o sortida d'un estudiant.
- Una classe que enregistra la llista de moviments (Servidor),
- Una tarjeta per als estudiants (EstudiantClient)
i
- Una tarjeta pels pares (PareClient).
|
|
|
|
|
|
 |
|
|
|
|
 |
Obre el
i crea un nou projecte amb el nom presencia.
Ves afegint les classes que es detallen més avall. Revisa el codi
de cadascuna per a verificar la feina que fan. |
|
|
|
|
|
A) Moviment |
|
|
|
|
|
|
Aquesta classe Moviment volem que reculli l'estructura
informativa de les entrades i sortides d'alumnes.
Què necessitem saber d'un moviment?
- qui el fa
- quan es fa
- si es tracta d'una entrada o d'una sortida.
Aquesta n'és una traducció en Java:
|
|
|
|
|
|
/**
* Estructura d'un moviment.
* Conté informació sobre qui fa el moviment, en quina data
* i si és entrada (entrada=true) o sortida (entrada=false)
*
* @author angel solans
* @version 15-04-2004
*/
public class Moviment {
// Codi d'estudiant
private String estudiant;
// Hora en que
es produeix el moviment
private java.util.Date hora;
// És
una entrada o una sortida?
private boolean entrada;
/**
* Al construir el moviment, assignar l'hora
actual i
* assignar el moviment a una entrada.
*/
public Moviment() {
estudiant="";
hora=new java.util.Date();
entrada=true;
}
/**
* Set del camp estudiant
*/
public void setEstudiant(String estudiant) {
this.estudiant=estudiant;
}
/**
* Set el camp entrada
* */
public void setEntrada(boolean entrada) {
this.entrada=entrada;
}
/**
* Get del camp estudiant
*/
public String getEstudiant() {
return this.estudiant;
}
/**
* Get del camp entrada
*/
public boolean isEntrada() {
return this.entrada;
}
/**
* Get del camp Hora
*/
public java.util.Date getHora() {
return this.hora;
}
}
|
|
|
|
|
|
Aquest tipus de classes, que descriuen la forma d'un objecte sense fer
res més i, per tant, contenen només constructor/s, camps
privats i mètodes accessors als camps (getters
i setters), reben
el nom de POJO (Plain Old Java Object). Moltes
eines de Java treballen amb pojos. És
bo, doncs, que sempre intentis descriure els objectes que fas en forma
de pojo.
Observa que definim tres camps: estudiant, hora i entrada.
- El primer és una cadena amb el codi d'estudiant. A la vida
real, aquest camp coincidiria amb la clau primària de la taula
d'estudiants de la base de dades.
- El segon és un objecte de la classe java.util.Date,
un contenidor de les dates i hores en què es produeixen les entrades
i sortides de l'edifici.
- Finalment, un camp lògic que es posa a cert
quan un moviment és una entrada i a fals
quan és una sortida.
La informació del constructor és important:
per defecte un moviment el definim com a entrada i, en crear l'objecte,
fem l'assignació de l'hora actual. Així no ens cal patir
per a gestionar l'hora en què es produeix el moviment: serà
la mateixa hora de la creació de l'objecte Moviment.
Com que aquesta hora ja no s'ha de canviar més, ja no hem escrit
un mètode "setter" -setHora()-
per aquest camp. Fixa't, en canvi, que sí que disposem d'un mètode
getHora().
|
|
|
|
|
|
B) Servidor |
|
|
|
|
|
|
Seguidament, creem una classe que s'encarrega d'enregistrar les entrades
i sortides d'alumnes i de facilitar la informació als pares. És
el cor del programa, la classe Servidor.
Afegeix-la al projecte presencia.
|
|
|
|
|
|
/**
* Gestiona el registre d'accessos a l'institut i
* en dóna la informació als pares
*
* @author Angel Solans
* @version 15-04-04
*/
public class Servidor {
//Llista d'accessos
del dia actual
private static java.util.Vector registre =
new java.util.Vector();
/**
* Només el servidor per defecte
*/
public Servidor() {
}
/**
* Entrada d'un estudiant a l'institut.
* S'enregistra l'estudiant que ha entrat
i l'hora
*
* @param estudiant codi de l'estudiant;
*/
public void entra(String estudiant) {
Moviment moviment = new
Moviment();
moviment.setEstudiant(estudiant);
registre.add(moviment);
}
/**
* Sortida d'un estudiant a l'institut.
* S'enregistra l'estudiant que ha sortit
i l'hora
*
* @param estudiant codi de l'estudiant;
*/
public void surt(String estudiant) {
Moviment moviment = new
Moviment();
moviment.setEntrada(false);
moviment.setEstudiant(estudiant);
registre.add(moviment);
}
/**
* Eina de supervisió dels pares.
* El servidor informa el pare dels moviments
del seu fill
* @param estudiant Codi de l'estudiant
*/
public void supervisa(String estudiant) {
java.text.SimpleDateFormat
df =
new
java.text.SimpleDateFormat
("dd-MM-yyyy
hh:mm aaa");
System.out.println
("---------------------------------------");
System.out.println("Activitat
del vostre fill/a");
System.out.println
("---------------------------------------");
java.util.Iterator iterador
= registre.iterator();
while (iterador.hasNext())
{
Moviment
moviment = (Moviment)iterador.next();
if
(moviment.getEstudiant().equals(estudiant)) {
String
cadena = "Sortida a les ";
if
(moviment.isEntrada()) cadena =
"Entrada a les ";
System.out.println(cadena+
df.format(moviment.getHora()));
}
}
}
/**
* Retorna el número de moviments
de la llista
* @return moviments del dia
*/
public int getMoviments() {
return registre.size();
}
/**
* Esborra les dades del vector
*/
public void neteja() {
registre.clear();
}
/**
*Llista totes les entrades d'alumnes
*/
public void getEntrades() {
java.text.SimpleDateFormat
df =
new
java.text.SimpleDateFormat
("dd-MM-yyyy
hh:mm aaa");
System.out.println("Registre
d'entrades");
System.out.println("-------------------");
if (registre!=null)
for
int (n=0;n<=registre.size();n++) {
if
( ((Moviment)registre.get(n)).isEntrada()) {
System.out.println(
((Moviment)registre.get(n)).getEstudiant()
);
System.out.println(df.format(
((Moviment)registre.get(n)).getHora())
);
}
}
}
}
|
|
|
|
|
|
Observa els detalls importants d'aquest codi:
- Novament camps static.
Recordes què passa quan a un camp d'una classe li posem l'etiqueta
static? Vol dir que el valor d'aquest camp
serà compartit per tots els objectes o instàncies de la
classe. En el nostre cas ens interessa utilitzar diferents objectes
de la classe Servidor (els alumnes en crearan
instàncies a l'entrar i sortir de l'institut i els pares quan
supervisin l'activitat dels seus fills) però volem que només
existeixi una única llista de moviments d'alumnes. La solució
és posar el camp de la llista com a static
i, a partir d'aquí, només hi haurà una llista per
a tothom.
- Vectors
Observa que la llista és un vector. Perquè no hem posat
una matriu? Com que no sabem el número d'entrades que tindrà
la llista no podem utilitzar classes de llista amb la longitud fixa.
En aquest mòdul has estudiat altres classes agrupadores que podríem
utilitzar en aquest programa. Pensa quines són!
El vector l'anirem omplint amb objectes de la classe Moviment.
Cada cop que un alumne entri o surti de l'institut es crea un objecte
Moviment que s'afegirà a la llista
única de moviments.
- La classe Servidor sap anotar entrades i
sortides a la llista i fer un resum dels moviments d'un alumne. Els
dos primers mètodes els utilitzen les targetes dels alumnes i
el darrer les targetes dels pares.
- Observa com es fa una entrada a l'institut. En primer lloc, creem
un moviment. Gràcies al constructor de la classe Moviment,
l 'objecte pren la data i hora actuals i s'assigna a la categoria d'entrades
a l'institut. Només ens cal assignar el moviment a l'estudiant
(moviment.setEstudiant(estudiant)) i afegir-lo
a la llista (registre.add(moviment)).
|
|
|
|
|
|
/**
* Entrada d'un estudiant a l'institut.
* S'enregistra l'estudiant que ha entrat i
l'hora
*
* @param estudiant codi de l'estudiant;
*/
public void entra(String estudiant) {
Moviment moviment = new
Moviment();
moviment.setEstudiant(estudiant);
registre.add(moviment);
}
|
|
|
|
|
|
- Les sortides són similars, només cal afegir aquesta
línia
|
|
|
|
|
|
moviment.setEntrada(false); |
|
|
|
|
|
per a indicar que el moviment és una sortida i no una entrada.
|
|
|
|
|
|
|
|
|
|
|
 |
Modifica el mètode supervisa
perquè ens retorni la data en els formats següents:
09 mayo 2004 12:08 PM
Domingo, 9 mayo 2004 12:08:56
|
|
|
|
|
|
C) Les targetes de pares i
estudiants |
|
|
|
|
|
|
La targeta dels estudiants només serveix per a fitxar a l'entrar
i al sortir de l'institut. Afegeix la classe al projecte:
|
|
|
/**
* Targeta de l'estudiant
*
* @author Angel Solans
* @version 15-04-2004
*/
public class EstudiantClient {
private String estudiant;
/**
* Mètode constructor per a objectes
de la classe
* EstudiantClient
* @param el codi d'estudiant
*/
public EstudiantClient(String estudiant) {
this.estudiant = estudiant;
}
/**
* Entrem a l'institut
*/
public void entra () {
Servidor servidor = new
Servidor();
servidor.entra(estudiant);
java.text.SimpleDateFormat
df =
new
java.text.SimpleDateFormat
("dd-MM-yyyy
hh:mm aaa");
System.out.println("Has
entrat a les "+
df.format(new java.util.Date()));
}
/**
* Sortim de l'institut
*/
public void surt () {
Servidor servidor = new
Servidor();
servidor.surt(estudiant);
java.text.SimpleDateFormat
df =
new
java.text.SimpleDateFormat
("dd-MM-yyyy
hh:mm aaa");
System.out.println("Has
sortit a les "+
df.format(new java.util.Date()));
}
}
|
|
|
|
|
|
Les targetes dels pares
només executes el mètode supervisa(),
que crida el mètode supervisa() del servidor.
Afegeix la classe al projecte: |
|
|
|
|
|
/**
* Targeta per als pares
*
* @author Angel Solans
* @version 15-04-2004
*/
public class PareClient {
//
Codi d'estudiant
private String estudiant;
/**
* Mètode constructor per a objectes
* de la classe PareClient
*/
public PareClient(String estudiant) {
this.estudiant = estudiant;
}
/**
* Obté la llista d'entrades i sortides
del fill
*
*/
public void supervisa () {
Servidor servidor = new
Servidor();
servidor.supervisa(estudiant);
}
}
|
|
|
|
|
|
- Posa en marxa l'aplicació.
- Crea dos estudiants i dónal's de clau "1" i "2"
respectivament.
- Crea també el pare de l'estudiant "1" obrint una
instància de la classe PareClient amb
un "1" com a paràmetre.
- Fes fitxar els alumnes executant diferents vegades els mètodes
entra() i surt().
- Finalment, posa't en el paper de pare i executa el mètode supervisa().
Obtindràs una sortida similar a aquesta:
|
|
|
|
|
|
El Depurador |
|
|
|
|
|
El codi del projecte de control de presència és senzill,
molt simple si el comparem a un projecte de la vida real. És fàcil
de depurar: si comets un error, segurament trobaràs l'origen amb
facilitat.
Però això no sempre és així.
Pràcticament tots els entorns de programació incorporen
eines de depuració per a facilitar la correcció i control
de les aplicacions. Alguns depuradors són molt sofisticats, altres
força senzills.
incorpora un depurador elemental, molt fàcil d'utilitzar. Malgrat
la pobra aparença, et resultarà més que suficient
per a treballar amb la major part de programes que puguis escriure.
La feina més habitual del depurador consisteix en posar
punts de ruptura en el programa.
- Si hem marcat un punt de ruptura en un fragment de codi i iniciem
la depuració, el programa s'executa fins arribar a la línia
marcada.
- Un cop aquí, s'atura l'execució del programa i el depurador
ens dóna el valor actual dels camps i les variables de la classe.
- A partir d'aquest moment, el programador controla l'execució
del programa; podem aturar l'aplicació, deixar-la córrer
fins al final o fer petits salts dins del codi per anar observant com
canvia l'estat de la classe i localitzar així els problemes.
- Els depuradors més potents permeten establir condicions per
aturar-se o no en una línia o ens deixen canviar els valors de
les variables sense reiniciar el programa.
Ara posarem el depurador en marxa. Abans, però, observa el mètode
següent que hem inclòs a la classe Servidor:
|
|
|
|
|
|
/**
*Llista totes les entrades d'alumnes
*/
public void getEntrades() {
java.text.SimpleDateFormat
df =
new
java.text.SimpleDateFormat
("dd-MM-yyyy
hh:mm aaa");
System.out.println("Registre
d'entrades");
System.out.println("-------------------");
if (registre!=null)
for
int n=0;n<=registre.size();n++) {
if
( ((Moviment)registre.get(n)).isEntrada()) {
System.out.println(
((Moviment)registre.get(n)).getEstudiant()
);
System.out.println(df.format(
((Moviment)registre.get(n)).getHora())
);
}
}
}
|
|
|
|
|
 |
Crea algunes targetes d'estudiant i fes alguns moviments, després
crea una instància de la classe Servidor
i executa el mètode getEntrades(). Obtindràs
un error en temps d'execució, un ArrayIndexOutOfBoundsException.
Tot i que estem convençuts que has trobat l'error en el codi d'un
cop d'ull, imagina que és la darrera hora de la teva jornada laboral
i estàs una mica espès: pots recórrer al depurador
per a localitzar l'error.
Obre la classe Servidor, activa l'editor i
ves al mètode getEntrades(). Faràs
que el flux normal del programa s'aturi en una línia determinada
per a repassar els valors de les variables i executar el programa línia
a línia fins a trobar l'error. Com que d'entrada no tens massa
idea d'on cau l'error, posaràs el punt de ruptura al començament
del mètode. Busca la línia
java.text.SimpleDateFormat
df =
new
java.text.SimpleDateFormat
("dd-MM-yyyy
hh:mm aaa");
en el mètode getEntrades(). És
la primera línia. Observa l'editor; a l'esquerra tens una petita
columna. Fes un clic sobre la columna a l'alçada de la línia
indicada. Si has encertat amb el ratolí, t'apareixerà un
senyal d'stop a l'esquerra de la línia: acabes de triar
un punt de ruptura pel programa.
Ara tanca l'editor i torna a la interfície principal del .
Tria l'opció de menú Veure | Mostrar Depurador (o
fes Ctrl + D). T'ha de sortir una finestra com aquesta:
|
|
|
|
|
|
|
|
|
|
|
|
És la finestra de depuració. Treballarem amb ella.
Torna a la finestra principal del
i posa el projecte a treballar si encara no ho has fet: crea alguns estudiants
i fes-los entrar a l'institut. Crea una instància de la classe
Servidor. Executa el mètode getEntrades().
Immediatament ens salta la finestra de depuració a primer pla:
|
|
|
|
|
|
 |
|
|
|
|
|
Ara ja pots manipular el depurador.
Observa que disposes de diferents caixes on es llisten, separadament
- Les variables estàtiques o camps static.
- Els camps o variables d'instància
- Les variables locals.
En el moment que obrim el depurador, només és actiu un
camp, la llista de moviments. És el vector que apareix a la caixa
de variables estàtiques. Fes un doble clic sobre el seu nom: s'obrirà
l'inspector d'objectes i podràs estudiar l'estat del vector.
Observa que el programa està aturat. Està esperant les
teves ordres perquè ara tens el control de flux de l'aplicació.
Clica sobre el botó Pas i el programa saltarà una
línia. Si a la línia següent hi ha una crida a un mètode,
picant sobre el botó Pas Endins el depurador entrarà
a l'interior del mètode cridat i també podràs depurar-lo.
Si vols que el programa continui amb normalitat, pica sobre el botó
Continuar.
Per a localitzar l'error en el mètode, fes correr el programa
pas a pas. Aniràs recorrent línies fins arribar al bucle
d'impressió de dades. Observa que, caminant pas a pas, pots extreure
totes les dades del vector fins que, arribant al darrer element, el bucle
intenta localitzar un moviment més enllà dels límits
del vector: aquest és l'error.
sinó
|
|
|
|
|
|
if
(registre!=null) for (int n=0;n<=registre.size();n++) { |
|
|
|
|
|
Has de modificar el
codi, no ha de ser |
|
|
|
|
|
n<=registre.size() |
|
|
|
|
|
sinó |
|
|
|
|
|
n<registre.size() |
|
|
|
|
|
de manera que la línia
ha de dir: |
|
|
|
|
|
if
(registre!=null) for (int n=0;n<registre.size();n++) |
|
|
|
&nbs |