Fondamenti di Informatica T2 Modulo 2

Realizzazione di Phone Plan
Università degli Studi di Bologna
Facoltà di Ingegneria
• Fare riferimento alla progettazione vista in aula
Fondamenti di Informatica T2
Modulo 2
• Partire implementando prima le classi con meno
dipendenze che, di conseguenza, sono più
semplici, nell’ordine: SimpleTime, Band,
Operator, PhonePlan.
Corso di Laurea in Ingegneria Informatica
Anno accademico 2008/2009
2
Realizzazione… e testing!
Soluzione - SimpleTime
package tlc;
• Usare Eclipse come ambiente di sviluppo
public class SimpleTime
{
private int hours;
private int minutes;
• Verificare di volta in volta quanto fatto fino alla
verifica completa del funzionamento del sistema
public static final SimpleTime MIN_VALUE = new
SimpleTime(0, 0);
public static final SimpleTime MAX_VALUE = new
SimpleTime(23, 59);
– Per ogni classe creata, verificarne il corretto
funzionamento
– Non eliminare i metodi di test: possono sempre servire!
public SimpleTime(int hours, int minutes)
{
this.hours = hours % 24;
Ore valide se comprese fra 0 a
23. Minuti validi se compresi fra
this.minutes = minutes % 60;
0 a 59.
}
…
• Usare il DayOfWeekEnum di cui al laboratorio
precedente!
– Naturalmente deve essere stato realizzato (!) e testato in
modo adeguato (!!)
3
Soluzione - SimpleTime
SimpleTime modella un orario
in modo semplice in termini di
ore e minuti.
…
public int getHours()
{
Metodi
Metodi accessor.
accessor
return hours;
}
public int getMinutes()
{
return minutes;
}
Restituisce un nuovo
public SimpleTime addMinutes(int mins)
orario con minuti pari alla
{
somma fra i minuti
int newHours;
dell’oggetto corrente ed i
minuti passati come
int newMinutes;
parametro.
newMinutes = this.minutes + mins;
newHours = this.hours + (newMinutes > 59 ? 1 : 0);
newMinutes %= 60;
return new SimpleTime(newHours, newMinutes);
}…
5
}
4
Soluzione - SimpleTime
Verifica se l’orario corrente è
…
valido controllando la
public boolean isValid()
consistenza del suo stato.
{
return this.compareTo(MIN_VALUE) >= 0 &&
this.compareTo(MAX_VALUE) <= 0;
}
public boolean equals(SimpleTime time)
{
if (time == null)
Verifica l’uguaglianza fra due
oggetti di tipo SimpleTime.
{
return false;
}
return this.hours == time.hours && this.minutes ==
time.minutes;
}
…
Università di Bologna – A.A. 2008/2009
6
Soluzione - SimpleTime
Soluzione - DayOfWeekEnum
…
public int compareTo(SimpleTime time)
{
Confronta due oggetti di tipo
if (this.hours > time.hours)
SimpleTime.
return 1;
else if (this.hours == time.hours)
{
if (this.minutes > time.minutes)
return 1;
else if (this.minutes == time.minutes)
return 0;
else
return -1;
}
else
return -1;
}
7
…
package tlc;
import java.util.GregorianCalendar;
Modella un giorno
della settimana.
public class DayOfWeekEnum
{
public static final DayOfWeekEnum SUNDAY = new
DayOfWeekEnum(GregorianCalendar.SUNDAY);
public static final DayOfWeekEnum MONDAY = new
DayOfWeekEnum(GregorianCalendar.MONDAY);
public static final DayOfWeekEnum TUESDAY = new
DayOfWeekEnum(GregorianCalendar.TUESDAY);
public static final DayOfWeekEnum WEDNESDAY = new
DayOfWeekEnum(GregorianCalendar.WEDNESDAY);
public static final DayOfWeekEnum THURSDAY = new
8
DayOfWeekEnum(GregorianCalendar.THURSDAY);
…
Soluzione - DayOfWeekEnum
Soluzione - DayOfWeekEnum
…
…
public static final DayOfWeekEnum FRIDAY = new
DayOfWeekEnum(GregorianCalendar.FRIDAY);
public static final DayOfWeekEnum SATURDAY = new
DayOfWeekEnum(GregorianCalendar.SATURDAY);
public int value;
Valore intero
associato al giorno
della settimana.
public DayOfWeekEnum(int gregoriaCalendarDay)
{
this.value = gregorianCalendarDay;
}
…
9
Soluzione - DayOfWeekEnum
public boolean isContainedIn(DayOfWeekEnum[] days)
{
for (DayOfWeekEnum day : days)
{
if (day == this)
Controlla se il giorno
{
corrente è contenuto
return true;
nell’insieme di giorni
}
passato come parametro.
}
return false;
}
public int toGregorianCalendarDayOfWeek()
{
return value;
}
…
package tlc;
Soluzione - Band
import java.util.Date;
import java.util.GregorianCalendar;
…
public static DayOfWeekEnum[] values()
{
return new DayOfWeekEnum[] {SUNDAY, MONDAY, TUESDAY,
WEDNESDAY, THURSDAY,
FRIDAY, SATURDAY};
}
public class Band
{
private DayOfWeekEnum[] combineDays;
private SimpleTime startTime;
private SimpleTime endTime;
private double costPerInterval;
public static DayOfWeekEnum
fromGregorianCalendarDayOfWeek(int gregorianCalendarDay)
{
for (DayOfWeekEnum day : DayOfWeekEnum.values())
if (day.value == gregorianCalendarDay)
return day;
return null;
}
11
10
Sfruttiamo le classi per la
gestione delle date e dei
calendari.
Ora fine e inizio validità
come SimpleTime e non
come Date.
public Band(SimpleTime startTime, SimpleTime endTime,
DayOfWeekEnum[] days, double costPerInterval)
{
Oggetto Band
caratterizzato da:
this.startTime = startTime;
ora
inizio / fine e
this.endTime = endTime;
giorni della
this.combinedDays = days;
settimana in cui la
this.costPerInterval = costPerInterval;
fascia oraria è
} …
valida, e costo
dell’intervallo.12
}
Università di Bologna – A.A. 2008/2009
Soluzione - Band
Soluzione - Band
…
public SimpleTime getStartTime()
{
return startTime;
}
public SimpleTime getEndTime()
{
return endTime;
}
…
public boolean isInBand(Date startCall)
{
GregorianCalendar calendar = new GregorianCalendar();
calendar.setTime(startCall);
Oggetto
calendar
per estrarre
il
Verifica
se la fascia
è
giorno,
l’ora, eper
i minuti
dalla data
applicabile
una data/ora
/orapassata
passata
come
parametro.
come
parametro
int day = calendar.get(GregorianCalendar.DAY_OF_WEEK);
int hour = calendar.get(GregorianCalendar.HOUR_OF_DAY);
int minute = calendar.get(GregorianCalendar.MINUTE);
Metodi
accessor.
public DayOfWeekEnum[] getCombinedDays()
{
return combinedDays;
}
public double getCostPerInterval()
{
return costPerInterval;
} …
SimpleTime startCallTime = new SimpleTime(hour, minute);
return
DayOfWeekEnum.fromGregorianCalendarDayOfWeek(day).
isContainedIn(combinedDays) &&
startTime.compareTo(startCallTime) <= 0 &&
endTime.compareTo(startCallTime) >= 0;
13
}
…
Soluzione - Band
Soluzione - Band
…
Verifica se la fascia corrente è
public boolean isValid()
valida controllando la
consistenza del suo stato.
{
if (combinedDays != null && combinedDays.length > 0 &&
startTime != null && endTime != null )
{
for (DayOfWeekEnum day : combinedDays)
{
if (day == null )
{
return false;
}
}
return startTime.compareTo(endTime) < 0;
}
return false;
}
15
…
Soluzione - Operator
…
public static void sortByStartTime(Band[] bands)
{
for (int i = 0; i < bands.length; i++)
{
for (int j = i + 1; j < bands.length; j++)
{
if (bands[i].startTime.compareTo(bands[j].startTime) > 0)
{
Band temp = bands[i];
bands[i] = bands[j];
Ordina l’array di fasce orarie
bands[j] = temp;
}
passate come parametro in
}
ordine crescente per ora di
}
inizio validità.
}
}
…
16
Soluzione - Operator
…
public String getName()
{
return name;
Metodi
Metodi accessor.
accessor
}
public Band[] getBands()
{
return bands;
}
public double getCostPerInterval(Date callStart)
{
Fornisce il costo per
for (Band b : bands)
una certa data / ora.
{
package tlc;
import java.util.Date;
public class Operator
{
private String name;
private Band[] bands;
14
Un operatore telefonico è
caratterizzato da un nome e da
un insieme di fasce applicabili.
public Operator(String name, Band[] bands)
{
this.name = name;
this.bands = bands;
}
…
if (b.isInBand(callStart))
return b.getCostPerInterval();
}
return -1;
}
17
} …
Università di Bologna – A.A. 2008/2009
18
Soluzione - Operator
…
public boolean isValid()
{
for (Band band : bands)
{
if (!band.isValid())
return false;
}
Soluzione - Operator
Verifica se l’operatore
corrente è valido controllando
la consistenza del suo stato. In
particolare, controlla ...
... che le bande siano
tutte valide e …
for (DayOfWeekEnum day : DayOfWeekEnum.values())
{
... che le bande coprino
if (!validateDay(day))
tutte le ore di tutti i
return false;
giorni della settimana.
}
return true;
}
…
19
… sfruttando
private boolean validateDay(DayOfWeekEnum day)
l’ordinamento
fra bande,
{
Seleziona le bande relative ad un
controlla che
if (day != null)
particolare giorno della settimana e ...
queste
{
coprano tutte
Band[] bandsInDay = selectBandInDay(day);
le ore del
Band.sortByStartTime(bandsInDay);
giorno.
for (int i = 0; i < bandsInDay.length - 1; i++)
{
SimpleTime firstBandEndTime =
bandsInDay[i].getEndTime();
SimpleTime secondBandStartTime =
bandsInDay[i + 1].
getStartTime();
if (!firstBandEndTime.addMinutes(1).
equals(secondBandStartTime))
return false;
}…
20
} …
Soluzione - Operator
Soluzione - Operator
Se l’insieme di bande coprono tutte le
ore del giorno, il metodo ritorna
true, altrimenti ...
…
return bandsInDay.length > 0 &&
bandsInDay[0].getStartTime().
equals(SimpleTime.MIN_VALUE) &&
bandsInDay[bandsInDay.length - 1].
getEndTime().equals(SimpleTime.MAX_VALUE);
…
private Band[] selectBandInDay(DayOfWeekEnum day)
{
Fornisce l’insieme di bande relative ad
int count = 0;
un particolare giorno della settimana.
for (Band band : bands)
if (day.isContainedIn(band.getCombinedDays()))
count++;
Band[] result = new Band[count];
int j = 0;
}
else
return false;
}
…
for (int i = 0; i < bands.length; i++)
if (day.isContainedIn(bands[i].getCombinedDays()))
result[j++] = bands[i];
… il metodo ritorna
false.
return result;
21
Soluzione - Plan
22
}
Soluzione - Plan
package tlc;
import java.util.Date;
Un piano telefonico è caratterizzato
public class Plan
dal nome del piano, dall’intervallo
{
“standard”, costo dello scatto alla
risposta,
e insieme degli operatori.
private String name;
private int interval;
private double startCallCost;
private Operator[] operators;
public Plan(String name, int interval, int startCallCost,
Operator[] operators)
{
this.name = name;
this.interval = interval;
this.startCallCost = startCallCost;
this.operators = operators;
} …
}
…
public String getName()
{
return name;
}
public int getInterval()
{
return interval;
}
Metodi
accessor.
public double getStartCallCost()
{
return startCallCost;
}
…
23
Università di Bologna – A.A. 2008/2009
24
Soluzione - Plan
Soluzione - Plan
…
public double getCallCost(Date callStart, Date callEnd,
String destOperator, String
destNumber)
Fornisce il costo di una chiamata
{
date le sue caratteristiche.
Operator selected = getOperatorByName(destOperator);
if (selected == null)
return -1;
double costPerInterval =
selected.getCostPerInterval(callStart);
if (costPerInterval == -1)
return -1;
long difference = callEnd.getTime() callStart.getTime();
int intervalCount = (int)(difference / interval);
25
return startCallCost + intervalCount * costPerInterval;
} …
Soluzione - Plan
…
public double getCallCost(PhoneCall call)
{
return getCallCost(call.getCallStart(),
call.getCallEnd(),
call.getDestOperator(), call.getDestNumber());
Fornisce il costo di una
}
chiamata dato un oggetto di
public boolean isValid()
tipo PhoneCall.
{
for (Operator operator : operators)
{
if (!operator.isValid())
Verifica se il piano telefonico
{
corrente è valido controllando
return false;
la validità degli operatori
}
telefonici.
}
return true;
}
26
…
package tlc;
…
private Operator getOperatorByName(String operatorName)
{
Operator selected = null;
import java.util.Date;
import java.text.DateFormat;
public class PhoneCall
{
private Date callStart;
private Date callEnd;
private String destOperator;
private String destNumber;
for (Operator o : operators)
{
if (o.getName().equals(operatorName))
{
selected = o;
Fornisce l’oggetto Operatore
break;
corrispondente al nome
}
passato come parametro.
}
return selected;
}
27
28
Soluzione - PhoneCall
…
public String toString()
{
DateFormat shortFormatter =
DateFormat.getDateTimeInstance(DateFormat.SHORT,
DateFormat.FULL);
Metodi
accessor.
return shortFormatter.format(callStart) + " -- " +
shortFormatter.format(callEnd) + " --> " +
public String getDestOperator()
{
return destOperator;
}
public String getDestNumber()
{
return destNumber;
} …
Una chiamata telefonica è
caratterizzata dalla data / ora di inizio
e fine, dall’operatore di
destinazione, e dal numero di
destinazione.
public PhoneCall(Date callStart, Date callEnd, String
destOperator, String destNumber)
{
this.callStart = callStart;
this.callEnd = callEnd;
this.destOperator = destOperator;
this.destNumber = destNumber;
}…
Soluzione - PhoneCall
…
public Date getCallStart()
{
return callStart;
}
public Date getCallEnd()
{
return callEnd;
}
Soluzione - PhoneCall
destNumber + “(" + destOperator + ”)";
}
Rappresentazione a stringa di
una chiamata telefonica.
29
Università di Bologna – A.A. 2008/2009
30
Soluzione - PhonePlanMain
Soluzione - PhonePlanMain
…
Come prima cosa
public static void main(String[] args)
imposta la cultura
{
locale, cioè quella
Locale.setDefault(Locale.ITALY);
italiana.
Plan plan = createPlan();
PhoneCall[] calls = getTestPhoneCalls();
NumberFormat formatter =
NumberFormat.getCurrencyInstance();
package tlc;
import
import
import
import
java.text.NumberFormat;
java.util.Date;
java.util.GregorianCalendar;
java.util.Locale;
public class PhonePlanMain
{
…
}
Classe per testare
l’applicazione.
31
Soluzione - PhonePlanMain
32
Crea le fasce telefoniche
relative ai giorni della
…
settimana
Band[] timBands = new Band[]{
new Band(new SimpleTime(0,0), new SimpleTime(23,59),
weekEnd, 0.10),
new Band(new SimpleTime(0,0), new SimpleTime(7,59),
workWeek, 0.10),
new Band(new SimpleTime(8,0), new SimpleTime(17,59),
workWeek, 0.20)
Crea l’operatore “TIM” con le
};
fasce appena create
DayOfWeekEnum[] weekEnd = new DayOfWeekEnum[]{
DayOfWeekEnum.SATURDAY,
DayOfWeekEnum.SUNDAY};
Crea i giorni della settimana
suddivisi in due insiemi: workWeek
e weekEnd.
piano plan.
Soluzione - PhonePlanMain
…
public static Plan createPlan()
{
DayOfWeekEnum[] workWeek = new DayOfWeekEnum[]{
DayOfWeekEnum.MONDAY,
DayOfWeekEnum.TUESDAY,
DayOfWeekEnum.WEDNESDAY,
DayOfWeekEnum.THURSDAY,
DayOfWeekEnum.FRIDAY};
…
}
…
Crea un nuovo piano
for (PhoneCall call : calls)
telefonico e un insieme di
chiamate
telefoniche di
{
test.
System.out.print(call);
System.out.print(" --- ");
System.out.println(formatter.format(
plan.getCallCost(call) / 100));
Stampa ciascuna chiamata ed
}
il costo della chiamata con il
Operator tim = new Operator("TIM", timBands);
…
33
Soluzione - PhonePlanMain
…
Crea un nuovo insieme di fasce
Band[] vodafoneBands = new Band[]
telefoniche relative ai giorni della
settimana
{
new Band(new SimpleTime(0,0), new SimpleTime(23,59),
weekEnd, 0.15),
new Band(new SimpleTime(0,0), new SimpleTime(7,59),
workWeek, 0.15),
new Band(new SimpleTime(8,0), new SimpleTime(17,59),
workWeek, 0.30)
Crea l’operatore “Vodafone” con le
};
fasce appena create
Operator vodafone = new Operator("Vodafone", vodafoneBands);
return new Plan("SuperConveniente", 1000, 10, new
Operator[] {tim,vodafone});
Crea un nuovo piano
} …
“SuperConveniente” con gli
35
operatori “TIM” e “Vodafone”.
34
Soluzione - PhonePlanMain
Crea e restituisce un
insieme di chiamate
telefoniche di test.
…
public static PhoneCall[] getTestPhoneCalls()
{
GregorianCalendar cal = new GregorianCalendar();
Date callStart, callEnd;
Crea una prima
chiamata verso
cal.set(2009, 02, 10, 15, 21, 21);
l’operatore
callStart = cal.getTime();
“TIM”, effettuata
il
10/02/2009,
cal.set(GregorianCalendar.HOUR_OF_DAY, 15);
dalle 15:21:21
cal.set(GregorianCalendar.MINUTE, 25);
alle 15:25:23.
cal.set(GregorianCalendar.SECOND, 23);
callEnd = cal.getTime();
PhoneCall call1 = new PhoneCall(callStart, callEnd,
"TIM",
"+39339123455679");
36
…
Università di Bologna – A.A. 2008/2009
Soluzione - PhonePlanMain
…
Soluzione - PhonePlanMain
Crea una seconda
chiamata verso
l’operatore
“Vodafone”.
…
cal.set(2009, 02, 10, 16, 35, 55);
callStart = cal.getTime();
cal.set(GregorianCalendar.HOUR_OF_DAY, 16);
cal.set(GregorianCalendar.MINUTE, 41);
cal.set(GregorianCalendar.SECOND, 23);
callEnd = cal.getTime();
PhoneCall call2 = new PhoneCall(callStart, callEnd,
”Vodafone”, "+39349987654321");
Crea una terza
chiamata verso
l’operatore “TIM”.
cal.set(2009, 02, 10, 17, 21, 21);
callStart = cal.getTime();
cal.set(GregorianCalendar.HOUR_OF_DAY, 18);
cal.set(GregorianCalendar.MINUTE, 25);
cal.set(GregorianCalendar.SECOND, 23);
callEnd = cal.getTime();
PhoneCall call3 = new PhoneCall(callStart, callEnd,
”TIM”, "+39339123455679”);
…
return new PhoneCall[] {call1, call2, call3};
}
37
Università di Bologna – A.A. 2008/2009
38