edutecnica

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.