8.7 (Caso di studio facoltativo) Pensare a oggetti

LA MULTIMEDIALITÀ: LE IMMAGINI, LE ANIMAZIONI E I SUONI
1
8.7 (Caso di studio facoltativo) Pensare a oggetti:
animazioni e suoni nella vista
Questo caso di studio si è focalizzato principalmente sul modello MVC della nostra simulazione di ascensore. Ora che abbiamo completato la progettazione del modello, ci occupiamo
della vista, che fornisce la presentazione visiva del modello. Nel nostro caso di studio, la vista,
incapsulata nella classe ElevatorView, è un oggetto JPanel contenente altri oggetti JPanel
“figli”, ognuno rappresentante un oggetto unico del modello MVC (per esempio, un oggetto
Person, un oggetto Button, ecc.). La classe ElevatorView è la classe più grande nel caso di
studio. In questa sezione, discutiamo le classi per la grafica e il suono usate da ElevatorView.
Presenteremo e spiegheremo il resto del codice sul sito Web www.apogeonline.com/libri/
02097/allegati/.
Nella sezione 3.7 del volume Tecniche di base, abbiamo costruito il diagramma di classe
del nostro modello individuando sostantivi e frasi dalla specifica del problema. Abbiamo
ignorato parecchi di questi sostantivi, perché non erano associati con il modello MVC. Ora,
elenchiamo i sostantivi e le frasi che sono relativi alla visualizzazione del modello MVC:
•
•
•
visualizzazione
audio
musica dell’ascensore.
Il sostantivo “visualizzazione” corrisponde alla vista, o alla presentazione visuale, del modello
MVC. Come descritto nella sezione 2.7, la classe ElevatorView aggrega diverse classi. Il
sostantivo “audio” si riferisce agli effetti sonori che la nostra simulazione genera quando
accadono varie azioni; creeremo la classe SoundEffects per generare tali effetti sonori. La
frase “musica dell’ascensore” si riferisce alla musica che viene riprodotta quando una persona
viaggia sull’ascensore; useremo la classe SoundEffects anche per riprodurre questa musica.
La vista visualizza gli oggetti del modello MVC. Creeremo la classe ImagePanel per
rappresentare gli oggetti stazionari del modello, come ElevatorShaft. Creeremo la classe
MovingPanel, che estende ImagePanel, per rappresentare gli oggetti in movimento, come
Elevator. Infine, creeremo la classe AnimatedPanel, che estende MovingPanel, per rappresentare gli oggetti in movimento la cui immagine corrispondente cambia continuamente,
come Person (useremo diverse immagini per mostrare la persona che cammina e preme un
pulsante). Usando queste classi, presentiamo il diagramma di classe della vista per la nostra
simulazione nella figura 8.5.
Le note indicano i ruoli che hanno le classi nel sistema. Secondo il diagramma di classe,
la classe ElevatorView rappresenta la vista, le classi ImagePanel, MovingPanel e AnimatedPanel
si riferiscono alla grafica e la classe SoundEffects si riferisce ai suoni. La classe ElevatorView
contiene diverse istanze delle classi ImagePanel, MovingPanel e AnimatedPanel e un’istanza della classe SoundEffects. Nella versione finale del caso di studio, assoceremo ogni oggetto del modello con una classe corrispondente nella vista.
In questa sezione, discutiamo le classi ImagePanel, MovingPanel e AnimatedPanel per
spiegare la grafica e le animazioni. Poi, discuteremo la classe SoundEffects per spiegare le
funzionalità audio.
2
CAPITOLO 8
javax.swing.JPanel
ImagePanel
1..*
1
ElevatorView
MovingPanel
1..*
1
1
AnimatedPanel
Grafica
Figura 8.5
Audio
1
1..*
1
SoundEffects
Vista
Diagramma di classe della vista del simulatore di ascensore
ImagePanel
La classe ElevatorView usa oggetti di sottoclassi di JPanel per rappresentare e visualizzare
ogni oggetto nel modello (come Elevator, Person, ecc.). La classe ImagePanel (figura 8.6)
è una sottoclasse di JPanel in grado di visualizzare un’immagine in una data posizione dello
schermo. La classe ElevatorView usa oggetti ImagePanel per rappresentare oggetti immobili nel modello, come ElevatorShaft e i due Floor. La classe ImagePanel contiene un
attributo intero, ID (riga 16), che dichiara un identificatore unico per tenere traccia
dell’ImagePanel nella vista. Ciò è utile quando diversi oggetti della stessa classe sono presenti nel modello, come diversi oggetti Person. La classe ImagePanel contiene l’oggetto position
della classe Point2D.Double (riga 19) per rappresentare la sua posizione sullo schermo. Vedremo più avanti che MovingPanel, che estende ImagePanel, dichiara la velocità con valori
double. Convertiremo le coordinate position in valori int per posizionare l’oggetto ImagePanel
sullo schermo (Java rappresenta le coordinate dello schermo come interi) nel metodo
setPosition (righe 90-94). La classe ImagePanel contiene anche un oggetto ImageIcon
chiamato imageIcon (riga 22), il cui metodo paintComponent (righe 54-60) visualizza l’icona
sullo schermo. Le righe 41-42 inizializzano ImageIcon usando un parametro stringa che
contiene il nome dell’immagine. Infine, la classe ImagePanel contiene l’oggetto panelChildren
della classe Set (riga 25) che memorizza tutti gli oggetti JPanel figli (o oggetti di una sottoclasse
di JPanel). Gli oggetti figli vengono visualizzati sopra il loro ImagePanel genitore: per
esempio, un oggetto Person dentro un Elevator. Il primo metodo add (righe 63-67) aggiunge un oggetto a panelChildren. Il secondo metodo add (righe 70-74) inserisce un
oggetto in panelChildren all’indice specificato. Il metodo setIcon (righe 84-87) imposta
imageIcon con una nuova immagine. Gli oggetti della classe AnimatedPanel usano
ripetutamente il metodo setIcon per cambiare l’immagine visualizzata, in modo da eseguire
l’animazione.
LA MULTIMEDIALITÀ: LE IMMAGINI, LE ANIMAZIONI E I SUONI
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
Figura 8.6
3
// ImagePanel.java
// sottoclasse di JPanel per posizionare e visualizzare ImageIcon
package com.deitel.jhtp5.elevator.view;
// package Java di base Java
import java.awt.*;
import java.awt.geom.*;
import java.util.*;
// package Java di estensione
import javax.swing.*;
public class ImagePanel extends JPanel {
// identificatore
private int ID;
// posizione sullo schermo
private Point2D.Double position;
// imageIcon da disegnare sullo schermo
private ImageIcon imageIcon;
// memorizza tutti i figli di ImagePanel
private Set panelChildren;
// costruttore che inizializza posizione e immagine
public ImagePanel( int identifier, String imageName )
{
super( null ); // specifica layout null
setOpaque( false ); // rende trasparente
// imposta identificatore unico
ID = identifier;
// imposta posizione
position = new Point2D.Double( 0, 0 );
setLocation( 0, 0 );
// crea ImageIcon con imageName dato
imageIcon = new ImageIcon(
getClass().getResource( imageName ) );
Image image = imageIcon.getImage();
setSize(
image.getWidth( this ), image.getHeight( this ) );
// crea Set per memorizzare i figli di Panel
La classe ImagePanel rappresenta e visualizza un oggetto immobile
del modello (continua)
4
CAPITOLO 8
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
Figura 8.6
panelChildren = new HashSet();
} // fine costruttore di ImagePanel
// disegna Panel sullo schermo
public void paintComponent( Graphics g )
{
super.paintComponent( g );
// se l’immagine è pronta, disegnala sullo schermo
imageIcon.paintIcon( this, g, 0, 0 );
}
// aggiunge figlio di ImagePanel a ImagePanel
public void add( ImagePanel panel )
{
panelChildren.add( panel );
super.add( panel );
}
// aggiunge figlio di ImagePanel a ImagePanel all’indice dato
public void add( ImagePanel panel, int index )
{
panelChildren.add( panel );
super.add( panel, index );
}
// rimuove figlio di ImagePanel da ImagePanel
public void remove( ImagePanel panel )
{
panelChildren.remove( panel );
super.remove( panel );
}
// imposta ImageIcon corrente da visualizzare
public void setIcon( ImageIcon icon )
{
imageIcon = icon;
}
// imposta posizione sullo schermo
public void setPosition( double x, double y )
{
position.setLocation( x, y );
setLocation( ( int ) x, ( int ) y );
}
// ritorna identificatore di ImagePanel
La classe ImagePanel rappresenta e visualizza un oggetto immobile
del modello (continua)
LA MULTIMEDIALITÀ: LE IMMAGINI, LE ANIMAZIONI E I SUONI
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
Figura 8.6
5
public int getID()
{
return ID;
}
// ottiene posizione di ImagePanel
public Point2D.Double getPosition()
{
return position;
}
// ottiene imageIcon
public ImageIcon getImageIcon()
{
return imageIcon;
}
// ottiene Set di figli di ImagePanel
public Set getChildren()
{
return panelChildren;
}
}
La classe ImagePanel rappresenta e visualizza un oggetto immobile
del modello
MovingPanel
La classe MovingPanel (figura 8.7) è una sottoclasse di ImagePanel in grado di cambiare la
sua posizione sullo schermo in base ai valori xVelocity e yVelocity (righe 20-21). La classe
ElevatorView usa oggetti MovingPanel per rappresentare oggetti mobili del modello, come
Elevator.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Figura 8.7
// MovingPanel.java
// Sottoclasse di JPanel con funzionalità di movimento sullo schermo
package com.deitel.jhtp5.elevator.view;
// package Java di base
import java.awt.*;
import java.awt.geom.*;
import java.util.*;
// package Java di estensione
import javax.swing.*;
public class MovingPanel extends ImagePanel {
La classe MovingPanel rappresenta e visualizza un oggetto che si muove
dal modello (continua)
6
CAPITOLO 8
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
Figura 8.7
// MovingPanel dovrebbe cambiare posizione?
private boolean moving;
// numero di pixel di cui MovingPanel si muove nelle direzioni
x e y
// per animationDelay millisecondi
private double xVelocity;
private double yVelocity;
// costruttore che inizializza posizione, velocità e immagine
public MovingPanel( int identifier, String imageName )
{
super( identifier, imageName );
// imposta velocità di MovingPanel
xVelocity = 0;
yVelocity = 0;
} // fine costruttore di MovingPanel
// aggiorna posizione e animazione di MovingPanel
public void animate()
{
// aggiorna posizione a seconda della velocità
if ( isMoving() ) {
double oldXPosition = getPosition().getX();
double oldYPosition = getPosition().getY();
setPosition( oldXPosition + xVelocity,
oldYPosition + yVelocity );
}
// aggiorna tutti i figli di MovingPanel
Iterator iterator = getChildren().iterator();
while ( iterator.hasNext() ) {
MovingPanel panel = ( MovingPanel ) iterator.next();
panel.animate();
}
} // fine metodo animate
// MovingPanel si sta muovendo sullo schermo?
public boolean isMoving()
{
return moving;
}
// imposta MovingPanel per muoversi sullo schermo
public void setMoving( boolean move )
La classe MovingPanel rappresenta e visualizza un oggetto che si muove
dal modello (continua)
LA MULTIMEDIALITÀ: LE IMMAGINI, LE ANIMAZIONI E I SUONI
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
Figura 8.7
7
{
moving = move;
}
// imposta le velocità x e y di MovingPanel
public void setVelocity( double x, double y )
{
xVelocity = x;
yVelocity = y;
}
// ritorna velocità x di MovingPanel
public double getXVelocity()
{
return xVelocity;
}
// ritorna velocità y di MovingPanel
public double getYVelocity()
{
return yVelocity;
}
}
La classe MovingPanel rappresenta e visualizza un oggetto che si muove
dal modello
Il metodo animate (righe 35-53) muove MovingPanel secondo i valori attuali dei campi
e yVelocity. Se la variabile boolean moving (riga 16) è true, le righe 38-44
usano i campi xVelocity e yVelocity per determinare la prossima posizione di movingPanel.
Le righe 47-52 ripetono il procedimento per tutti i figli. Nella nostra simulazione, ElevatorView
invoca il metodo animate e il metodo paintComponent della classe ImagePanel ogni 50
millisecondi. Queste chiamate in rapida successione spostano l’oggetto MovingPanel.
xVelocity
AnimatedPanel
La classe AnimatedPanel (figura 8.8), che estende la classe MovingPanel, rappresenta un
oggetto animato del modello (cioè, oggetti in movimento la cui immagine corrispondente
cambia continuamente), come Person. La classe ElevatorView anima un AnimatedPanel
cambiando l’immagine associata con imageIcon.
1
2
3
4
5
6
7
Figura 8.8
// AnimatedPanel.java
// Sottoclasse di Panel con funzionalità di animazione
package com.deitel.jhtp5.elevator.view;
// package Java di base
import java.awt.*;
import java.util.*;
La classe AnimatedPanel rappresenta e visualizza un oggetto animato
dal modello (continua)
8
CAPITOLO 8
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
Figura 8.8
// package Java di estensione
import javax.swing.*;
public class AnimatedPanel extends MovingPanel {
// ImageIcon deve essere animato?
private boolean animating;
// frequenza di cambio immagine
private int animationRate;
private int animationRateCounter;
private boolean cycleForward = true;
// ImageIcon individuali usati per l’animazione
private ImageIcon imageIcons[];
// memorizza tutte le sequenze di frame
private java.util.List frameSequences;
private int currentAnimation;
// deve continuare l’animazione alla fine del ciclo?
private boolean loop;
// deve visualizzare l’ultima immagine alla fine dell’animazione?
private boolean displayLastFrame;
// determina la prossima immagine da visualizzare
private int currentFrameCounter;
// costruttore che prende un array di nomi di file
public AnimatedPanel( int identifier, String imageName[] )
{
super( identifier, imageName[0] );
// crea oggetti ImageIcon dall’array di stringhe imageName
imageIcons = new ImageIcon[ imageName.length ];
for ( int i = 0; i < imageIcons.length; i++ ) {
imageIcons[i] = new ImageIcon(
getClass().getResource( imageName[i] ) );
}
frameSequences = new ArrayList();
} // fine costruttore di AnimatedPanel
// aggiorna posizione dell’icona e immagine dell’animazione
public void animate()
La classe AnimatedPanel rappresenta e visualizza un oggetto animato
dal modello (continua)
LA MULTIMEDIALITÀ: LE IMMAGINI, LE ANIMAZIONI E I SUONI
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
Figura 8.8
{
super.animate();
// visualizza la prossima immagine se counter > frequenza
di animazione
if ( frameSequences != null && isAnimating() ) {
if ( animationRateCounter > animationRate ) {
animationRateCounter = 0;
determineNextFrame();
}
else
animationRateCounter++;
}
} // fine metodo animate
// determina prossima immagine dell’animazione
private void determineNextFrame()
{
int frameSequence[] =
( int[] ) frameSequences.get( currentAnimation );
// se non ci sono più immagini, determina immagine finale
// a meno che non si debba ripetere l’animazione
if ( currentFrameCounter >= frameSequence.length ) {
currentFrameCounter = 0;
// se loop è false, termina animazione
if ( !isLoop() ) {
setAnimating( false );
if ( isDisplayLastFrame() )
// visualizza ultima immagine della sequenza
currentFrameCounter = frameSequence.length - 1;
}
}
// imposta immagine corrente dell’animazione
setCurrentFrame( frameSequence[ currentFrameCounter ] );
currentFrameCounter++;
} // fine metodo determineNextFrame
// aggiunge animazione all’ArrayList frameSequences
public void addFrameSequence( int frameSequence[] )
{
frameSequences.add( frameSequence );
La classe AnimatedPanel rappresenta e visualizza un oggetto animato
dal modello (continua)
9
10
CAPITOLO 8
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
Figura 8.8
}
// chiede se AnimatedPanel sta eseguendo l’animazione
public boolean isAnimating()
{
return animating;
}
// imposta AnimatedPanel per eseguire l’animazione
public void setAnimating( boolean animate )
{
animating = animate;
}
// imposta ImageIcon corrente
public void setCurrentFrame( int frame )
{
setIcon( imageIcons[ frame ] );
}
// imposta frequenza di animazione
public void setAnimationRate( int rate )
{
animationRate = rate;
}
// ottiene frequenza di animazione
public int getAnimationRate()
{
return animationRate;
}
// imposta se l’animazione deve ripetersi
public void setLoop( boolean loopAnimation
{
loop = loopAnimation;
}
)
// ottiene se l’animazione deve ripetersi
public boolean isLoop()
{
return loop;
}
// ottiene se visualizzare l’ultima immagine alla fine
dell’animazione
private boolean isDisplayLastFrame()
La classe AnimatedPanel rappresenta e visualizza un oggetto animato
dal modello (continua)
LA MULTIMEDIALITÀ: LE IMMAGINI, LE ANIMAZIONI E I SUONI
151
152
153
154
155
11
{
return displayLastFrame;
}
// imposta se visualizzare l’ultima immagine alla fine
dell’animazione
public void setDisplayLastFrame( boolean displayFrame )
{
displayLastFrame = displayFrame;
}
156
157
158
159
160
161
162
163
164
165
166
167
168
// inizia ad eseguire la sequenza di animazione all’indice dato
public void playAnimation( int frameSequence )
{
currentAnimation = frameSequence;
currentFrameCounter = 0;
setAnimating( true );
}
}
Figura 8.8
La classe AnimatedPanel rappresenta e visualizza un oggetto animato
dal modello
La classe AnimatedPanel sceglie un oggetto ImageIcon da disegnare sullo schermo tra
diversi oggetti ImageIcon memorizzati nell’array imageIcons (riga 23). La classe AnimatedPanel
determina l’oggetto ImageIcon secondo una sequenza di riferimenti, memorizzata nella lista
frameSequences (riga 26), che è un array di interi che memorizza la sequenza appropriata
per visualizzare gli oggetti ImageIcon: in particolare, ogni intero rappresenta l’indice di un
oggetto ImageIcon nell’array imageIcons. La figura 8.9 mostra la relazione tra imageIcons
e frameSequences (questo non è un diagramma UML). Per esempio, la sequenza numero
2 = { 2, 1, 0 }
si riferisce a { imageIcon[2], imageIcon[1], imageIcon[0] }, che porta alla sequenza
di immagini { C, B, A }. Nella vista, ogni immagine è un file .png unico. Il metodo
addFrameSequences (righe 102-105) aggiunge una sequenza di animazione alla lista
frameSequences. Il metodo playAnimation (righe 162-167) fa partire l’animazione asso-
frameSequences
imageIcons
A
B
C
D
0
1
2
3
Figura 8.9
0=
0
1
2
1=
0
1
3
2=
2
1
0
3=
3
2
2
1
0
0
image sequences
A
B C
A
B
D
C
B
A
D C
C
Relazione tra l’array imageIcons e la lista FrameSequences
B
A
A
12
CAPITOLO 8
ciata con il parametro frameSequences. Per esempio, supponiamo di avere un oggetto
AnimatedPanel chiamato personAnimatedPanel nella classe ElevatorView. Il frammento di codice
animatedPanel.playAnimation( 1 );
genererebbe la sequenza di immagini { A, B, D, B, A } se usiamo la figura 8.9 come
riferimento.
Il metodo animate (righe 56-70) sovrascrive il metodo animate della superclasse
MovingPanel. Le righe 61-69 determinano la successiva immagine per l’animazione a seconda del campo animationRate, che è inversamente proporzionale alla velocità dell’animazione: un valore più alto per animationRate comporta un’animazione più lenta. Per esempio,
se animationRate vale 5, animate si sposta alla successiva immagine dell’animazione ogni
cinque volte viene invocato. Usando questa logica, la frequenza di animazione viene massimizzata
quando animationRate ha un valore 1, perché la successiva immagine viene determinata
ogni volta che viene chiamato animate.
Il metodo animate chiama determineNextFrame (righe 73-99) per determinare la successiva immagine da visualizzare: in particolare, viene chiamato il metodo setCurrentFrame
(righe 120-123) che imposta imageIcon (l’immagine correntemente visualizzata) all’immagine ritornata dalla corrente sequenza di animazione. Le righe 84-92 di determineNextFrame
vengono usate per ripetere l’animazione. Se loop vale false, l’animazione termina dopo una
iterazione. L’ultima immagine della sequenza viene visualizzata se displayLastFrame vale
true, e, se vale false, viene visualizzata la prima immagine della sequenza. Se loop vale
true, l’animazione si ripete finché non viene fermata esplicitamente.
Effetti sonori
Vediamo ora come generare dei suoni nella nostra simulazione. La classe SoundEffects
(figura 8.10) trasforma file audio (.au), wave (.wav) e MIDI (.mid) contenenti suoni come
il campanello, i passi della persona e la musica dell’ascensore, in oggetti java.applet.AudioClip.
L’oggetto ElevatorView riprodurrà gli oggetti AudioClip per generare i suoni. Tutti i file
dei suoni sono nella struttura di directory
com/deitel/jhtp5/elevator/view/sounds
Nella nostra simulazione, useremo suoni e file MIDI disponibili gratuitamente al sito Web di
Microsoft:
msdn.microsoft.com/downloads/default.asp
Per scaricare questi suoni, fate clic su “Graphics and Multimedia”, “Multimedia (General)”, e
poi “Sounds”.
La classe SoundEffects contiene i metodi getAudioClip (righe 16-27), che usa il metodo statico newAudioClip (della classe java.applet.Applet) per ritornare un oggetto
AudioClip usando il parametro soundFile. Il metodo setPathPrefix (righe 30-33) permette di cambiare directory del file audio (utile se vogliamo dividere i nostri file sonori tra
directory diverse).
LA MULTIMEDIALITÀ: LE IMMAGINI, LE ANIMAZIONI E I SUONI
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
Figura 8.10
13
// SoundEffects.java
// Ritorna oggetti AudioClip
package com.deitel.jhtp5.elevator.view;
// package Java di base
import java.applet.*;
public class SoundEffects {
// posizione dei file audio
private String prefix = “”;
public SoundEffects() {}
// ottiene AudioClip associato con soundFile
public AudioClip getAudioClip( String soundFile )
{
try {
return Applet.newAudioClip( getClass().getResource(
prefix + soundFile ) );
}
// ritorna null se il file audio non esiste
catch ( NullPointerException nullPointerException ) {
return null;
}
}
// imposta prefisso per la posizione di soundFile
public void setPathPrefix( String string )
{
prefix = string;
}
}
La classe SoundEffects ritorna oggetti AudioClip
Conclusione
Avete appena completato un procedimento di progettazione orientata agli oggetti che aveva
lo scopo di prepararvi per le sfide dei progetti di livello industriale. Speriamo che abbiate
trovato le sezioni “Pensare a oggetti” informative e utili come complemento al materiale
presentato nei vari capitoli. Inoltre, speriamo vi siate divertiti a progettare il sistema usando
UML. Il linguaggio UML è stato adottato come standard dall’industria del software mondiale per la modellazione di software orientato agli oggetti.
Anche se avete completato la fase di progettazione, avete solo sfiorato il processo di
implementazione. Consultate il sito Web www.apogeonline.com/libri/02097/allegati/ per la completa implementazione del progetto, e le traduzioni dei diagrammi UML in
un programma Java completo per la simulazione dell’ascensore. Studiare l’implementazione
rafforzerà le abilità nella programmazione che avete sviluppato leggendo il libro e migliorerà la vostra comprensione del processo di progettazione.