Esercizio 6
All'aggiusteria agricola possono essere riparati tre tipi di apparecchiature: motozappe, decespugliatori, tosaerba. Ciascuna di queste macchine, ha dei dati comuni.
Un numero intero che costituisce il numero d'ordine della lavorazione, ogni volta che una macchina viene consegnata all'aggiusteria acquisisce un nuovo numero d'ordine, anche se quella macchina è già stata lì in precedenza. Deve essere specificata la marca dell'apparecchio. Ad ogni macchina deve essere associato il totale del costo di riparazione.
Per i tosaerba e le motozappe va specificato il numero di ruote della macchina.
Per i decespugliatori bisogna specificare se l'accensione è elettronica oppure no.
Costruire una opportuna gerarchia di classi, strutturando correttamente la base dei dati.
Il massimo numero di macchine che possonono essere simultaneamente in lavorazione è 10 perché questi sono i posti in officina.
Ad ogni macchina riparata, deve essere associata una lista delle lavorazioni effettuate e del loro costo, la cui somma costituisce il totale del costo della riparazione .
All'aggiusteria agricola possono essere
riparati tre tipi di apparecchi:
M=motozappe
D=decespugliatori
T=tosaerba
Ciascuna di queste macchine, ha dei dati comuni:
int id :un intero che il numero d'ordine della lavorazione
int tot :il costo totale della riparazione
String m: la marca dell'apparecchio.
I tosaerba e le motozappe hanno un dato specifico che è
int nruote :il numero di ruote che ha la
macchina .
I decespugliatori hanno un dato specifico che è
boolean ae:se il decespugliatore è ad accensione
elettronica ae=true.
In base alle regole della programmazione ad oggetti, facciamo in
modo che i dati comuni siano tutti contenuti in un'unica classe
genitrice A (apparecchio), da questa verranno derivate le tre classi
figlie M (motozappa) T (tosaerba) e D (decespugliatore) dove vengono
inseriti solo i dati specifici dei singoli apparecchi.
Il codice minimo che definisce questa strutturazione è il seguente:
class aggiusteria{
public static void main(String[] args) {
M m=new M(1,"bosch",2);
D d=new D(2,"honda",true);
T t=new T(3,"viking",4);
System.out.println(m);
System.out.println(d);
System.out.println(t);
}//fine main
}//fine classe
class A{
private int id;
private int tot;//totale costo riparazione
private String marca;
A(int jd,String m){id=jd;tot=0;marca=m;}
public String toString(){
String s="id:"+id+" marca:"+marca+" tot:"+tot;
return s;
}
}//fine classe A
class M extends A {
private int nruote;
M(int jd,String m,int n){super(jd,m);nruote=n;}
public String toString(){
String s=super.toString()+" num ruote:"+nruote;
return s;
}
}//fine classe M
class T extends A {
private int nruote;
T(int jd,String m,int n){super(jd,m);nruote=n;}
public String toString(){
String s=super.toString()+" num ruote:"+nruote;
return s;
}
}//fine classe T
class D extends A {
private boolean ae;
D(int jd,String m,boolean ele){ super(jd,m);ae=ele;}
public String toString(){
String s=super.toString()+" acc.el:"+ae; return
s;
}
}//fine classe D
Quando si crea una classe figlio, ad es.un tosaerba si usa l'istruzione
T t=new T(3,"viking",4);
l'istruzione è completa di tutti i dati necessari.
Quando questi dati arrivano al costruttore di T si ha l'istruzione
T(int jd,String m,int n){super(jd,m);nruote=n;}
come si vede i dati jd, m
vengono usati dall'istruzione super. super,
invoca il costruttore della classe genitrice A:
solo la classe genitrice può memorizzare l'id e
la marca dell'apparecchio, mentre le ruote rimangono assegnate all'attributo
privato nruote interno alla classe T.
Il costruttore della classe A è:
A(int jd,String m){id=jd;tot=0;marca=m;}
Notiamo che nella chiamata alla superclasse, ad es.
super(jd,m);
non viene passato il valore tot relativo
al totale della lavorazione, questo perché quando la macchina entra
in officina, ancora non si conoscono quanti e quali interventi bisogna
effettuare: l'attributo privato tot viene
inizializzato a zero di default dal costruttore (della classe A).
L'officina può ospitare al massimo 10 macchine, per cui, nel programma
principale dobbiamo predisporre un vettore di 10 elementi di classe
A.
A V[]=new A[10];
Se nel vettore dobbiamo inserire un tosaerba, usiamo una istruzione
del tipo
V[i]=new T(maxid,m,n);
Questo è possibile perché un oggetto di classe T
è implicitamente un oggetto di classe A dato
che è un suo discendente.
Questo vettore, potrà poi, essere manipolato da opportuni metodi
statici per operazioni di inserimento, cancellazione, aggiornamento.
Non è opportuno usare dei vettori standard per questo genere di
problema, sarebbe meglio usare delle liste dinamiche, come vectorlist,arraylist
etc. che non hanno limiti di dimensione.
Una operazione importante è il numero d'ordine da assegnare ad una
eventuale macchina entrante, per questo predisponiamo una variabile
globale che chiamiamo maxid e che dovrà
automaticamente auto incrementarsi ad ogni nuova commessa inserita.
La distinta delle riparazioni è chiaramente una struttura record,
in quanto deve essere dotata di una voce relativa alla riparazione
effettuata (rip) e ad un prezzo relativo
(prz). Questa struttura record viene implementata
dalla classe L.
N.B.:la classe L non è legata alla
gerarchia delle classi già esistenti , non vi è
la necessità; essa viene usata solo per definire un record
di dati; è l'equivalente di una scrittura in linguaggio C:
struct L{
char rip[80];
int prz;
}lista[10];
Il vettore che chiamiamo lista [] di oggetti
di classe L, viene aggiunto fra gli attributi
privati della classe A.
Il prezzo di ogni singola riparazione andrà ad incrementare l'attributo
tot che, come detto, costituisce il totale
del costo della commessa. La nuova struttura dei dati è riportata
qui sotto
:
Dal disegno si vede come sia stato necessario introdurre un metodo
getid() per la restituzione del numero d'ordine; questa necessità
esiste quando si vuole eliminare un certo ordine che è stato evaso.
Inoltre nella classe A, è stato introdotto
il metodo di istanza setlista() per inserire
tutte le voci delle riparazioni fatte con i rispettivi costi.
Uno degli inconvenienti che ci sono, nell'usare dei vettori in questo
contesto, è che quando viene inizializzato, il vettore V[],
ha tutti i suoi elementi impostati a null,
cioè in uno stato indefinito.
Per evitare errori del tipo Java.lang.NullPointerException si preferisce
inizializzare tutti gli elementi del vettore V[]
con generici oggetti di classe A tramite
l'istruzione
for(int i=0;i < V.length;i++) V[i]=new M(0,"",0);
All'inizio del programma tutti gli elementi del vettore vengono,
dunque, portati in una stato 'finito' in particolare con numero
d'ordine id=0.
Lo stesso avviene nel costruttore della classe A,
per gli elementi della lista delle riparazioni:
for(int i=0;i < lista.length;i++) lista[i]=new L("",0);
I posti disponibili nel vettore V[] saranno
riconoscibili perché dotati di un id=0, mentre
nel vettore lista[] perché hanno un attributo
prz=0.
Esiste poi, una procedura di eliminazione eli()
che fa riferimento al numero d'ordine id;
questo fa si che l'indice del vettore sia, in generale, diverso
dal numero d'ordine.
L'elemento eliminato viene sostituito da un oggetto 'allo stato
0' di cui abbiamo già detto.
V[j]=new M(0,"",0);
Questo, crea di conseguenza, una frammentazione dei dati nel vettore
Si preferisce, allora, comprimere (shrink) quest'ultimo tramite
la routine
for(j=0;j < V.length-1;j++)
if(V[j].getid()==0){
V[j]=V[j+1];
V[j+1]=new M(0,"",0);
}
la procedura di inserimento ins() dovrà solo
cercare la prima occorrenza nulla id=0 per
individuare la locazione, dove inserire il nuovo ordine.
Questa soluzione (ne sono possibili altre) potrà poi permettere,
l'introduzione di altre classi relative ad altri tipi di apparecchiatura.
import java.util.Scanner;
class aggiusteria{
public static int maxid=0;
public static void main(String[] args) {
Scanner in=new Scanner(System.in);
char ch;
A V[]=new A[10];
for(int i=0;i < V.length;i++)V[i]=new M(0,"",0);
do{
System.out.println("[i]nserisci\n[m]odifica\n[e]limina\ne[x]it:");
ch=in.next().toLowerCase().charAt(0);
switch(ch){
case 'i':{ins(V);print(V);break;}
case 'm':{mod(V);print(V);break;}
case 'e':{eli(V);print(V);break;}
case 'x':{System.out.println("fine
prog.");break;}
default:{System.out.println("scelta
non valida");break;}
}//fine switch
}while(ch!='x');
}//fine main
static void eli(A V[]){ //elimina
int i,j=0;
Scanner in=new Scanner(System.in);
System.out.print("ins.id:");
i=in.nextInt();
for(j=0;j < V.length;j++)
if(V[j].getid()==i){V[j]=new M(0,"",0);break;}
for(j=0;j < V.length-1;j++)//compressione
vettore
if(V[j].getid()==0){
V[j]=V[j+1];
V[j+1]=new M(0,"",0);
}
}//fine eli
static void mod(A V[]){ //modifica
char ch;
String voce;
int prezzo,i,j=0,id=-1;
Scanner in=new Scanner(System.in); System.out.print("ins.id:");i=in.nextInt();
for(j=0;j < V.length;j++)
if(V[j].getid()==i){id=j;break;}
do{
System.out.print("ins.voce:");voce=in.next();
System.out.print("ins.prezzo:");prezzo=in.nextInt();
V[id].setlista(voce,prezzo);
System.out.print("Continuare?(S/N):");
ch=in.next().toUpperCase().charAt(0);
}while(ch=='S');
}//fine mod
static void print(A V[]){ //stampa
for(int j=0;j < V.length;j++) if(V[j].getid()!=0)System.out.println(V[j]);
}//fine print
static void ins(A V[]){ //inserisci
Scanner in=new Scanner(System.in);
String m;
char ch;
int n=4,i=0;
boolean e=false;
for(i=0;i < V.length;i++)
if(V[i].getid()==0)break;
do{
System.out.print("[M]otozappa [T]osaerba [D]ecesp.
e[X]it:");
ch=in.next().toUpperCase().charAt(0);
if(ch=='D' || ch=='M' || ch=='T'){
System.out.print("ins.marca:");m=in.next();
maxid++;
if(ch!='D'){
System.out.print("ins.n.ruote:");n=in.nextInt();
if(ch=='M')V[i]=new
M(maxid,m,n);
else V[i]=new
T(maxid,m,n);
}else{
System.out.print("acc.elettr.(0/1)?:");n=in.nextInt();
if(n!=0)e=true;
else e=false;
V[i]=new D(maxid,m,e);
}
i++;
}//fine if
}while(ch!='X' && i < 10);
}// fine ins
}//fine classe
class A{
private int id;
private int tot;//totale costo riparazione
private String marca;
L lista[];
A(int jd,String m){//costruttore
id=jd;
tot=0;
marca=m;
lista=new L[10];
for(int i=0;i < lista.length;i++)lista[i]=new
L("",0);
}//fine costruttore
public int getid(){return id;}
public void setlista(String v,int p){
for(int j=0;j < lista.length;j++)
if(lista[j].getprz()==0){
lista[j]=new L(v,p);
tot+=p;//incremento
il totale del costo della riparazione
break;
}
}//fine setlista
public String toString(){
String s="id:"+id+" marca:"+marca+" tot:"+tot;
if(lista[0].getprz()!=0)s+="\n";
for(int j=0;j < lista.length;j++) if(lista[j].getprz()!=0)s+=lista[j];
return s; }
}//fine classe A
class M extends A {
private int nruote;
M(int jd,String m,int n){super(jd,m);nruote=n;}
public String toString(){
String s=super.toString()+" num ruote:"+nruote;
return s; }
}//fine classe M
class T extends A {
private int nruote;
T(int jd,String m,int n){super(jd,m);nruote=n;}
public String toString(){
String s=super.toString()+" num ruote:"+nruote;
return s; }
}//fine classe T
class D extends A {
private boolean ae;
D(int jd,String m,boolean ele){super(jd,m);ae=ele;}
public String toString(){
String s=super.toString()+" acc.el:"+ae;
return s; }
}//fine classe D
class L {
String rip;
int prz; L(String r,int p){rip=r;prz=p;}
public int getprz(){return prz;}
public String toString(){
String s="\tvoce:"+rip+" costo:"+prz+"\n";
return s; }
}//fine classe L
Nel programma, mancano tutte le routine per intercettare la fuoriuscita dai limiti del vettore (ArrayIndexOutOfBoundsException) che andranno scritte in modo opportuno.