|  | 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) 
          iUna 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.
 
 
VectorsObserva 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 PMDomingo, 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'estudiantprivate 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ànciaLes 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 |