Sei sulla pagina 1di 7

Ingegneria del Software — Soluzione del Tema 15/02/2021

Esercizio 1
Si consideri la seguente classe Java PackageManager per gestire l’installazione di pacchetti software e delle loro
dipendenze. I singoli pacchetti sono rappresentati dalla classe pura Package di cui riportiamo i metodi rilevanti.
p u b l i c c l a s s P a c k a g e Ma n a g e r {
/ / R i t o r n a l o s p a z i o d i s c o d i s p o n i b i l e i n KByte .
p u b l i c / *@ p u r e @* / i n t a v a i l a b l e S i z e ( ) ;

/ / R i t o r n a t r u e s e e s o l o s e dep e ’ una d i p e n d e n z a d i r e t t a o i n d i r e t t a d i p e
/ / dep non e ’ a t t u a l m e n t e i n s t a l l a t a .
p u b l i c / *@ p u r e @* / b o o l e a n d e p T o I n s t a l l ( P a c k a g e dep , P a c k a g e p ) ;

/ / Se p non e ’ g i a ’ i n s t a l l a t o e c ’ e ’ s u f f i c i e n t e s p a z i o d i s c o , i n s t a l l a p e t u t t i
/ / i p a c c h e t t i da c u i p d i p e n d e d i r e t t a m e n t e o i n d i r e t t a m e n t e e c h e non s o n o i n s t a l l a t i .
/ / Se non c ’ e ’ s u f f i c i e n t e s p a z i o d i s c o l a n c i a l ’ e c c e z i o n e D i s k F u l l E x c e p t i o n .
p u b l i c v o i d i n s t a l l ( P a c k a g e p ) throws D i s k F u l l E x c e p t i o n ;

/ / Ritorna l ’ elenco dei pacchetti attualmente i n s t a l l a t i


p u b l i c / *@ p u r e @* / S e t <Package> g e t I n s t a l l e d ( ) ;
}

p u b l i c / *@ p u r e @* / c l a s s P a c k a g e {
/ / Ritorna l ’ i d e n t i f i c a t i v o univoco del pacchetto software
public String getId ( ) ;

/ / R i t o r n a la dimensione d e l p a c c h e t t o s o f t w a r e in KBytes
public int getSize ( ) ;

/ / R i t o r n a l ’ e l e n c o d e i p a c c h e t t i da c u i q u e s t o p a c c h e t t o d i p e n d e d i r e t t a m e n t e
p u b l i c L i s t <Package> g e t D e p s ( ) ;

/ / Due p a c c h e t t i s o n o u g u a l i s e hanno l o s t e s s o i d
public boolean e q u a l s ( Object o ) ;
}

Domanda a)
Si specifichi in JML il metodo depToInstall().

Soluzione
/ / @ r e q u i r e s dep != n u l l && p != n u l l
/ / @ensures \ r e s u l t <==> (
/ /@ ( p . e q u a l s ( dep ) && ! g e t I n s t a l l e d ( ) . c o n t a i n s ( p ) ) | |
/ /@ ( ( \ e x i s t s Package d ; p . g e t D e p s ( ) . c o n t a i n s ( d ) ; d e p T o I n s t a l l ( dep , d ) ) ) )
p u b l i c / *@ p u r e @* / b o o l e a n d e p T o I n s t a l l ( P a c k a g e dep , P a c k a g e p ) ;

Domanda b)
Si specifichi in JML il metodo install().

Soluzione
/ / @ r e q u i r e s p != n u l l
/ /@
/ / @ensures
/ /@ ( \ sum Package dep ; \ o l d ( d e p T o I n s t a l l ( dep , p ) ) ; dep . s i z e ( ) ) <= \ o l d ( a v a i l a b l e S i z e ( ) ) &&
/ /@ ( \ f o r a l l Package x ; ; g e t I n s t a l l e d ( ) . c o n t a i n s ( x ) <==>
/ /@ ( \ o l d ( g e t I n s t a l l e d ( ) ) . c o n t a i n s ( x ) | | d e p T o I n s t a l l ( x , p ) ) ) &&
/ /@ a v a i l a b l e S i z e ( ) == \ o l d ( a v a i l a b l e S i z e ( ) ) −
/ /@ ( \ sum Package dep ; d e p T o I n s t a l l ( dep , p ) ; dep . s i z e ( ) )
/ /@
/ / @ s i g n a l s D i s k F u l l E x c e p t i o n &&
/ /@ ( \ sum Package dep ; d e p T o I n s t a l l ( dep , p ) ; dep . s i z e ( ) ) > \ o l d ( a v a i l a b l e S i z e ( ) ) &&
/ /@ g e t I n s t a l l e d ( ) . c o n t a i n s A l l ( \ o l d ( g e t I n s t a l l e d ( ) ) ) &&
/ /@ \ o l d ( g e t I n s t a l l e d ( ) ) . c o n t a i n s A l l ( g e t I n s t a l l e d ( ) ) &&
/ /@ \ o l d ( a v a i l a b l e S i z e ( ) ) == a v a i l a b l e S i z e ( )
p u b l i c / *@ p u r e @* / v o i d i n s t a l l ( P a c k a g e p ) throws D i s k F u l l E x c p e t i o n ;

Domanda c)
Si consideri un’implementazione che utilizza una lista per contenere i pacchetti installati e un intero per mantenere lo
spazio disponibile, come mostrato di seguito.
p u b l i c c l a s s P a c k a g e Ma n a g e r {
p r i v a t e f i n a l L i s t <Package> l i s t ;
private int availableSize ;
... }

Per tale implementazione, si definiscano l’invariante di rappresentazione e la corrispondente funzione di astrazione.

Soluzione
Nell’invariante di rappresentazione escludiamo che la lista sia nulla, che contenga valori nulli e che contenga duplicati.
Richiediamo che lo spazio disponibile sia maggiore o uguale a zero
/ /@( * R e p r e s e n t a t i o n i n v a r i a n t RI * )
/ / @ p r i v a t e i n v a r i a n t l i s t != n u l l && a v a i l a b l e S i z e >= 0 &&
/ /@ ( \ f o r a l l i n t i ; i >=0 && i < l i s t . s i z e ( ) ; l i s t . g e t ( i ) != n u l l
/ /@ ( \ f o r a l l i n t j ; j >=0 && j <i ; ! l i s t . g e t ( j ) . e q u a l s ( l i s t . g e t ( i ) ) ) ) &&

La funzione di astrazione definisce getInstalled() in funzione della lista usata nella rappresentazione e
availableSize() in funzione della variabile corrispondente. Gli altri metodi pubblici sono infatti definiti a partire da
essi.
/ /@( * A b s t r a c t i o n f u n c t i o n AF * )
/ / @ p r i v a t e i n v a r i a n t a v a i l a b l e S i z e ( ) == a v a i l a b l e S i z e &&
/ /@( \ f o r a l l Package p ; p != n u l l ;
/ /@ g e t I n s t a l l e d ( ) . c o n t a i n s ( p ) <==> l i s t . c o n t a i n s ( p ) )

Domanda d)
Si consideri ora una classe PackageManager2 che aggiunge un metodo uninstall(Package p) per cancellare
un pacchetto se questo è installato. È possibile definire PackageManager2 come sottoclasse di PackageManager
in accordo con il principio di sostituzione?

Soluzione
Non è possibile, in quanto l’aggiunta del metodo violerebbe un invariante della classe, ovvero il fatto che i pacchetti non
possono essere rimossi.
Esercizio 2
Si consideri la seguente classe Java:
public c l a s s Matrix {
p r i v a t e double [ ] [ ] d a t a ;
private int size ;

public Matrix ( int s i z e ) {


this . size = size ;
d a t a = new d o u b l e [ s i z e ] [ s i z e ] ;
}

p u b l i c s y n c h r o n i z e d d o u b l e g e t ( i n t row , i n t column ) {
r e t u r n d a t a [ row ] [ column ] ;
}

p u b l i c s y n c h r o n i z e d v o i d s e t ( i n t row , i n t column , d o u b l e v a l u e ) {
d a t a [ row ] [ column ] = v a l u e ;
}

p u b l i c s y n c h r o n i z e d v o i d add ( M a t r i x m) {
f o r ( i n t r = 0 ; r<s i z e ; r ++) {
f o r ( i n t c = 0 ; c<s i z e ; c ++) {
d a t a [ r ] [ c ] += m. d a t a [ r ] [ c ] ;
}
}
}

public synchronized void c l e a r ( ) {


f o r ( i n t r = 0 ; r<s i z e ; r ++) {
f o r ( i n t c = 0 ; c<s i z e ; c ++) {
data [ r ][ c ] = 0;
}
}
}
}

Domanda a)
La classe è correttamente sincronizzata? In caso negativo si illustri come modificare la classe per risolvere il problema di
sincronizzazione identificato.

Soluzione
Il metodo add accede a m.data senza sincronizzarsi su m. La seguente versione modificata del metodo aggira il problema
senza rischiare di cadere in una situazione opposta, di potenziale deadlock:
p u b l i c v o i d add ( M a t r i x m) {
f o r ( i n t r = 0 ; r<s i z e ; r ++) {
f o r ( i n t c = 0 ; c<s i z e ; c ++) {
s e t ( r , c , g e t ( r , c ) +m. g e t ( r , c ) ) ;
}
}
}

Domanda b)
Si aggiunga un metodo public void asyncPrint() che stampa a video il contenuto della matrice in un thread
separato rispetto al chiamante. Ovvero, il chiamante dovrà tornare subito mentre un thread separato esegue la vera e
propria stampa.

Soluzione
Aggiungiamo un metodo privato print che esegue la stampa (deve essere sincronizzato poichè accede allo stato della
matrice), mentre il metodo asyncPrint (non è necessario che sia sincronizzato) si occuperà di creare ed avviare il
thread separato che esegue print:
public void a s y n c P r i n t ( ) {
new T h r e a d ( ( ) −> p r i n t ( ) ) . s t a r t ( ) ;
/ * Oppure :
new T h r e a d ( ) {
p u b l i c v o i d run ( ) {
print ();
}
}. s t a r t ( ) ;
*/
}

private synchronized void p r i n t ( ) {


f o r ( i n t r = 0 ; r<s i z e ; r ++) {
f o r ( i n t c = 0 ; c<s i z e ; c ++) {
System . o u t . p r i n t f ( ” %10.1 f ” , d a t a [ r ] [ c ] ) ;
}
System . o u t . p r i n t l n ( ” ” ) ;
}
}

Domanda c)
Si supponga che il metodo add non sia presente e si modifichi il metodo set in maniera che il chiamante venga sospeso
in attesa che il valore in posizione row,column diventi zero prima di impostarlo al valore value.
Si indichi se altri metodi devono essere modificati e come (si ricorda che il metodo add è stato eliminato).

Soluzione
Il metodo set deve essere modificato come segue:
p r i v a t e s y n c h r o n i z e d v o i d s e t ( i n t row , i n t column , d o u b l e v a l u e ) throws I n t e r r u p t e d E x c e p t i o n {
w h i l e ( d a t a [ row ] [ column ] ! = 0 ) w a i t ( ) ;
d a t a [ row ] [ column ] = v a l u e ;
}

Inoltre bisogna aggiungere l’istruzione notifyAll() al metodo clear.


Esercizio 3
Si consideri il seguente programma Java.
public abstract c l a s s Vector {
p u b l i c a b s t r a c t d o u b l e norm ( ) ;
}

public c l a s s PlanarVector extends Vector {


p r o t e c t e d f i n a l double x ;
p r o t e c t e d f i n a l double y ;
p u b l i c P l a n a r V e c t o r ( d o u b l e x , d o u b l e y ) { t h i s . x=x ; t h i s . y=y ; }
p u b l i c d o u b l e norm ( ) { r e t u r n Math . s q r t ( x * x+y * y ) ; }
p u b l i c P l a n a r V e c t o r sum ( P l a n a r V e c t o r v ) { r e t u r n new P l a n a r V e c t o r ( x+v . x , y+v . y ) ; }
p u b l i c S t r i n g t o S t r i n g ( ) { r e t u r n ” x=” + x + ” y=” + y ; }
}

public c l a s s SpaceVector extends PlanarVector {


p r o t e c t e d f i n a l double z ;
p u b l i c S p a c e V e c t o r ( d o u b l e x , d o u b l e y , d o u b l e z ) { s u p e r ( x , y ) ; t h i s . z=z ; }
p u b l i c d o u b l e norm ( ) { r e t u r n Math . s q r t ( x * x+y * y+z * z ) ; }
p u b l i c S p a c e V e c t o r sum ( S p a c e V e c t o r v ) { r e t u r n new S p a c e V e c t o r ( x+v . x , y+v . y , z+v . z ) ;
}
p u b l i c S t r i n g t o S t r i n g ( ) { r e t u r n s u p e r . t o S t r i n g ( ) + ” z=” + z ; }
}

e la seguente classe di prova (situata nello stesso package).


1 public class TestVector {
2 p u b l i c s t a t i c v o i d main ( S t r i n g [ ] a r g s ) {
3 V e c t o r v0 ;
4 P l a n a r V e c t o r v1 , v2 ;
5 S p a c e V e c t o r v3 , v4 ;
6 v1 = new P l a n a r V e c t o r ( 3 , 4 ) ;
7 v0 = v1 ;
8 v2 = new S p a c e V e c t o r ( 2 , 3 , 6 ) ;
9 v3 = new S p a c e V e c t o r ( 2 , 1 , 0 ) ;
10 v4 = v3 ;
11 System . o u t . p r i n t l n ( v0 . sum ( v1 ) ) ;
12 System . o u t . p r i n t l n ( v1 . sum ( v2 ) ) ;
13 System . o u t . p r i n t l n ( v2 . sum ( v1 ) ) ;
14 System . o u t . p r i n t l n ( v2 . sum ( v3 ) ) ;
15 System . o u t . p r i n t l n ( v3 . sum ( v2 ) ) ;
16 System . o u t . p r i n t l n ( v3 . sum ( v4 ) ) ;
17 System . o u t . p r i n t l n ( v1 . norm ( ) ) ;
18 System . o u t . p r i n t l n ( v2 . norm ( ) ) ;
19 }
20 }

Domanda a)
Si elenchino i numeri di riga corrispondenti alle eventuali istruzioni che generano errori di compilazione, giustificando la
risposta.
Dopo avere eliminato le istruzioni corrispondenti, si scriva il risultato dell’esecuzione del programma (riportando esplici-
tamente i numeri di riga originali). Qualora un risultato non sia intero, lo si rappresenti simbolicamente, senza calcolarne
il valore (es. “radice di 2”).

Soluzione
L’unica istruzione da eliminare è quella alla riga 11, in quanto il tipo statico di v0 (ossia Vector) non ha il metodo
sum.
Il risultato dell’esecuzione è il seguente:
12 X= 5 . 0 y =7.0
13 x =5.0 y =7.0
14 x =4.0 y =4.0
15 x =4.0 y =4.0
16 x =4.0 y =2.0 z =0.0
17 5.0
18 7.0
Esercizio 4
Si consideri il seguente metodo statico Java.
1 static int test ( int x) {
2 i n t y = x%3;
3 System . o u t . p r i n t l n ( y ) ;
4 w h i l e ( x>0 && y <3) {
5 i f ( y ==1)
6 return y ;
7 y − −;
8 }
9 return 0;
10 }

Si determini un insieme minimo di test per ciascuno dei seguenti criteri di copertura. Qualora un criterio non sia
soddisfacibile, calcolare la percentuale di copertura dell’insieme di test definito per quel criterio.

Domanda a)
Copertura delle istruzioni.

Soluzione
Servono almeno due casi di test, in quanto vi sono due return. Ad esempio, x=2 (o x=5) esegue il cammino
1,2,3,4,5,7,4,5,6. Il caso x=0 copre anche la riga 9 (ignoriamo le righe 8 e 10 che sono solo dei terminatori
sintattici).

Domanda b)
Copertura delle decisioni (branch).

Soluzione
I due casi precedenti vanno bene.

Domanda c)
Copertura delle condizioni e decisioni.

Soluzione
La copertura totale delle condizioni è impossibile, perché il resto della divisione per 3 è sempre minore di 3. Usando i due
casi precedenti è quindi possibile coprire solo 2 condizioni su 3 (66%).
Domanda d)
In base eventualmente anche ai casi di test definiti, individuare la presenza di un possibile errore di
programmazione.

Soluzione
Il programma entra in loop infinito quando x%3 è zero. Quindi ad esempio per x=3.

Potrebbero piacerti anche