Un esercizio

Esempio di esercizio al calcolatore dell'anno scorso.

Gli esercizi d'esame saranno anche su cose fatte nelle prossime settimane.


Testo del compito

Si vuole realizzare un programma per la gestione delle camere di un albergo. Per ogni camera, si deve memorizzare il suo numero, il fatto che la camera sia occupata o meno, e in tal caso, il nome della persona che la occupa. Dal momento che le camere hanno prezzi diversi, va anche memorizzato il prezzo, nel caso in cui la camera è occupata.

Il testo è diviso in due parti.


Parte 1.

Scrivere una classe Camera in modo che ogni istanza rappresenti un singolo oggetto, e che abbia i seguenti metodi:

Camera(int, String, boolean, int)
costruttore
int getNumero()
ritorna il numero della stanza
String getNome()
ritorna il nome della persona che occupa attualmente la stanza, oppure la stringa vuota se la camera è libera;
boolean getOccupata()
restituisce il valore true se la camera è attualmente occupata;
int getPrezzo()
restituisce il prezzo a cui la camera è stata data;
void occupa(String nome, int prezzo)
occupa la stanza con il nome della persona, a un certo prezzo;
void stampa()
stampa i dati di una camera;
Camera read(BufferedReader)
legge i dati di una camera da un canale di input già aperto; i dati degli oggetti sono nel seguente formato:
numero della prima stanza
occupazione
persona occupante
prezzo
numero della seconda stanza
...

Se la stanza è occupata, il nome dell'occupante non è significativo; ogni stanza occupa comunque quattro righe nel file; se il canale di input termina, il metodo deve restituire null; il dato occupazione è la stringa "occupata" oppure "libera"

Nota: il nome della persona è memorizzato come una unica stringa.


Parte 2.

Scrivere una classe Albergo che gestisce l'insieme di tutte le stanze dell'albergo. I dati si trovano memorizzati in un file. La classe deve implementare i seguenti metodi:

Albergo(String)
costruttore: prende come argomento il nome del file che contiene i dati
void stampaOccupate(int p)
stampa l'elenco della stanze attualmente occupate e che hanno un prezzo inferiore a p
int totalePrezzo()
dice la cifra totale che l'albergo riceverà per tutte le stanze,

Aiuto!

Si fa una parte per volta.

Per ogni parte, si va per ordine.

Non c'è scritto, ma si assume che le classi devono essere incapsulate.


Programma di prova

Il programma di prova e un file di esempio vengono dati:

import java.io.*;

class ProvaCamera {
  public static void main(String arg[]) throws Exception {
    FileReader r=new FileReader("lista.txt");
    BufferedReader b=new BufferedReader(r); 

    Camera c, d;
    c=Camera.read(b);
    d=Camera.read(b);
    c.stampa();
    d.stampa();

		// stampa il nome dell'occupante della prima
    System.out.println(c.getNome());

		// occupa la seconda stanza
    d.occupa("Francesco Totti", 80);


		// stampa di nuovo la seconda stanza
    d.stampa();
  }
}

Viene anche dato il file lista.txt


Le componenti degli oggetti Camera

I dati del problema:

Testo Cosa va fatto
Per ogni camera, si deve memorizzare ... classe Camera
il suo numero, il fatto che la camera sia occupata o meno, e in tal caso, il nome della persona che la occupa componenti: numero, booleano, stringa
Dal momento che le camere hanno prezzi diversi, va anche memorizzato il prezzo, nel caso in cui la camera è occupata. componente intera (prezzo)

Il fatto che le componenti nome e prezzo non siano significative quando la stanza è libera è irrilevante.

In ogni caso, queste componenti mi servono.

Se la camera non è occupata, non le uso.


Inizio definizione della classe

Componenti:

class Camera {
  private int numero;
  private boolean occupata;
  private String nome;
  private int prezzo;

  // metodi
}

Voglio una classe incapsulata, quindi tutte le componenti devono essere private.

Dopo aver fatto questo, provare a compilare.

Non è bene fare tutto e poi compilare: se ci sono errori, non si capisce dove sono.


Costruttore

Inizializza l'oggetto con i parametri passati.

class Camera {
  private int numero;
  private boolean occupata;
  private String nome;
  private int prezzo;

  Camera(int n, String nome, boolean o, int p) {
    this.numero=n;
    this.occupata=o;
    this.nome=nome;
    this.prezzo=p;
  }

  // altri metodi
}

Solito costruttore con parametri.

Compilare anche a questo punto.

Per provare, fare una di queste due cose:


Le intestazioni

Sulla base dei nomi dei metodi, e dei loro argomenti, riesco a scrivere tutte le intestazioni.

Se i metodi tornano un valore, tornare una costante:

import java.io.*;

class Camera {
  private int numero;
  private boolean occupata;
  private String nome;
  private int prezzo;

  Camera(int n, String nome, boolean o, int p) {
    this.numero=n;
    this.occupata=o;
    this.nome=nome;
    this.prezzo=p;
  }

  int getNumero() {
    return 0;
  }

  boolean getOccupata() {
    return false;
  }

  String getNome() {
    return "";
  }

  int getPrezzo() {
    return 0;
  }

  void occupa(String nome, int prezzo) {
  }

  void stampa() {
  }

  static Camera read(BufferedReader b) throws IOException {
    return new Camera(0, "", false, 12);
  }
}

Questo permette di provare subito la classe.

Poi metteremo le istruzioni un metodo per volta.


Metodi facili

I metodi get... restituiscono il valore di una componente:

class Camera {
  private int numero;
  private boolean occupata;
  private String nome;
  private int prezzo;

  // costruttore

  int getNumero() {
    return this.numero;
  }

  boolean getOccupata() {
    return this.occupata;
  }

  int getPrezzo() {
    return this.prezzo;
  }

  // altri metodi
}

Unico caso particolare: il metodo getNome:

  String getNome() {
    if(this.occupata)
      return this.nome;
    else
      return "";
  }

Compilare

Si può anche provare con il programma: commentare le invocazioni di metodi che non sono stati ancora definiti nella classe.


Altri metodi

Occupa mette dei valori in due componenti dell'oggetto.

  void occupa(String nome, int prezzo) {
    this.occupata=true;
    this.nome=nome;
    this.prezzo=prezzo;
  }

Metodo di stampa:

  void stampa() {
    System.out.print("["+this.numero+", "+this.nome+", ");
    System.out.println(this.occupata+", "+this.prezzo+"]");
  }

Metodo di lettura

Ho un oggetto BufferedReader.

Per leggere una linea, basta invocare readLine su questo oggetto.

Devo mettere import java.io.*; e throws IOException

È un metodo statico: l'oggetto Camera viene creato.

import java.io.*;

class Camera {
  // componenti e altri metodi

  static Camera read(BufferedReader b)
  throws IOException {
    int n, p;
    String nome;
    boolean o;

    String s;

    s=b.readLine();
    if(s==null)
      return null;
    n=Integer.parseInt(s);

    s=b.readLine();
    if(s==null)
      return null;
    if(s.equals("occupata"))
      o=true;
    else
      o=false;

    s=b.readLine();
    if(s==null)
      return null;
    nome=s;

    s=b.readLine();
    if(s==null)
      return null;
    p=Integer.parseInt(s);

    return new Camera(n, nome, o, p);
  }
}

Nota: nel file, c'è prima l'occupazione e poi il nome.

Invece il costruttore ha questi due parametri nell'ordine opposto.


Fine prima parte

E con questo, la prima parte è fatta!

Per provarla, compilare anche ProvaCamera.java, che già si trova sul disco.

Anche il file di prova lista.txt si trova già sul disco.


Seconda parte

Non iniziare la seconda parte se la prima non è stata terminata!

Il programma di prova si chiama ProvaAlbergo.java


L'oggetto

L'albergo ha come unica componente il nome del file in cui si trovano i dati:

import java.io.*;

class Albergo {
  private String nomefile;

  // costruttore e metodi
}

Questo vale per tutti gli esercizi d'esame: si assume che ogni oggetto sia la rappresentazione di un file.


Costruttore

È il costruttore standard:

import java.io.*;

class Albergo {
  private String nomefile;

  Albergo(String s) {
    this.nomefile=s;
  }

  // metodi
}

Spesso, la definizione del costruttore suggerisce le componenti.

Se il costruttore ha come argomento una stringa che è un nome di file, allora la stringa potrebbe essere una componente dell'oggetto.


Metodi: ciclo

I due metodi fanno una operazione su tutti gli oggetti del file.

Lettura di un oggetto da file: Camera.read(b), dove b è un BufferedReader

Devo quindi:


Creazione del BufferedReader

Come al solito:

import java.io.*;

class Albergo {
  void stampaOccupate(int p) throws IOException {
    FileReader r=new FileReader(this.nomefile);
    BufferedReader b=new BufferedReader(r);

    ...
  }
}

Ciclo di lettura

Il ciclo standard è:

  while(true) {
    leggi elemento;

    se e' null
      break;

    fai qualcosa con l'elemento
  }

Qui si legge un oggetto Camera invece dell'oggetto String


Stampa di tutti gli elementi

Stampiamo tutti gli elementi:

  void stampaTutte() throws IOException {
    FileReader r=new FileReader(this.nomefile);
    BufferedReader b=new BufferedReader(r);

    Camera c;

    while(true) {
      c=Camera.read(b);

      if(c==null)
        break;

      c.stampa();
    }
  }

Il metodo vero

Devo stampare solo le camere occupate che abbiano prezzo minore di p

Il ciclo è lo stesso; cambia solo l'operazione che faccio sull'oggetto.

  void stampaOccupate(int p) throws IOException {
    FileReader r=new FileReader(nomefile);
    BufferedReader b=new BufferedReader(r);

    Camera c;

    while(true) {
      c=Camera.read(b);
      if(c==null)
        break;
      if(c.getOccupata() && (c.getPrezzo()<p) )
        c.stampa();
    }
  }

Faccio il secondo?

No, perchè il primo sicuramente contiene errori sintattici.

Compilare la classe solo con il primo.

Eseguire il programma di prova:


Secondo metodo

Stessa struttura.

Si tratta di un totale

Uso il metodo dell'accumulatore (risultato parziale)

Il ciclo è sempre quello; cambia solo l'operazione da fare sugli oggetti.

  int totalePrezzo() throws IOException {
    FileReader r=new FileReader(nomefile);
    BufferedReader b=new BufferedReader(r);

    Camera c;
    int somma=0;

    while(true) {
      c=Camera.read(b);

      if(c==null)
        break;

      if(c.getOccupata())
        somma=somma+c.getPrezzo();
    }

    return somma;
  }

A parte int somma=0 e if(...), è lo stesso ciclo di prima.