Anteprima
Vedrai una selezione di 7 pagine su 30
Time Range - Case Study Pag. 1 Time Range - Case Study Pag. 2
Anteprima di 7 pagg. su 30.
Scarica il documento per vederlo tutto.
Time Range - Case Study Pag. 6
Anteprima di 7 pagg. su 30.
Scarica il documento per vederlo tutto.
Time Range - Case Study Pag. 11
Anteprima di 7 pagg. su 30.
Scarica il documento per vederlo tutto.
Time Range - Case Study Pag. 16
Anteprima di 7 pagg. su 30.
Scarica il documento per vederlo tutto.
Time Range - Case Study Pag. 21
Anteprima di 7 pagg. su 30.
Scarica il documento per vederlo tutto.
Time Range - Case Study Pag. 26
1 su 30
Disdici quando vuoi 162x117
Disdici quando
vuoi
Acquista con carta
o PayPal
Scarica i documenti
tutte le volte che vuoi
Sintesi

TimeRange: Case study sull'implementazione di un valore



Introduzione



In questo tutorial utilizzo un semplice problema di programmazione come mezzo per affrontare le
problematiche connesse all'implementazione di un nuovo tipo di dato. L'obiettivo finale è quello
di realizzare un tipo analogo a TimeSpan , il quale incapsula un intervallo di tempo.
A partire dalla definizione del problema, procederò per gradi, utilizzando dapprima un approccio
procedurale, per arrivare progressivamente a un corretta soluzione OO. Metterò in evidenza i limiti

delle soluzioni adottate di volta in volta, mostrando come superarli e dimostrando che l'imple-
mentazione di TimeSpan (o tipo analogo) rappresenta il risultato ideale.

(Nota a margine: non realizzerò un'implementazione identica a quella di TimeSpan , ma una molto
più semplice e che non prende in considerazione le gestione dell'overflow di valori interi)

Un approccio procedurale



Davanti a un problema simile la prima questione è: come rappresentare i dati. Naturalmente, il
modello scelto deve avere la funzione di favorire l'implementazione delle richieste del problema:
visualizzazione delle informazioni sugli atleti ed elaborazioni sulla base dei loro tempi.
Di seguito fornisco alcune opzioni, delle quali le prime due sono volutamente inadeguate. (Ma
che evidenziano l'importanza di fornire un'appropriata rappresentazione dei dati.)
Estratto del documento

6.1.3 Risoluzione temporale e intervallo di variazione..................................................29

6.2 “Immutabilità” di TimeRange...................................................................29

TimeRange 3 di 30

1 Introduzione

In questo tutorial utilizzo un semplice problema di programmazione come mezzo per affrontare le

problematiche connesse all'implementazione di un nuovo tipo di dato. L'obiettivo finale è quello

di realizzare un tipo analogo a , il quale incapsula un intervallo di tempo.

TimeSpan

A partire dalla definizione del problema, procederò per gradi, utilizzando dapprima un approccio

procedurale, per arrivare progressivamente a un corretta soluzione OO. Metterò in evidenza i limiti

delle soluzioni adottate di volta in volta, mostrando come superarli e dimostrando che l'imple-

mentazione di (o tipo analogo) rappresenta il risultato ideale.

TimeSpan

(Nota a margine: non realizzerò un'implementazione identica a quella di , ma una molto

TimeSpan

più semplice e che non prende in considerazione le gestione dell'overflow di valori interi)

1.1 Problema: NYC Marathon

Si desiderano elaborare i tempi dei partecipanti dalla gara della maratona olimpica. Questi sono

memorizzati in un file CSV:

Cenci Leonardo, Italia, 4:06:16

Kamworor Geoffrey, Kenya, 2:10:23

Ghirmay Ghebreslassie, Eritrea, 2:11:4

Feyisa Lilesa, Etiopia, 2:09:54

Eliud Kipchoge, Kenya, 2:8:44

Munyo Mutai, Uganda, 2:11:49

Galen Rupp, USA, 2:10:05

Alphonce Simbu, Tanzania, 2:11:15

Jared Ward, USA, 2:11:30

...

Occorre eseguire le seguenti operazioni:

1. Visualizzare l'elenco in ordine di classifica (in base ai tempi).

2. Visualizzare la differenza tra il tempo migliore e quello peggiore.

Vincoli

Non è possibile utilizzare il tipo .

TimeSpan

(Nota bene: per semplicità non si fa riferimento ai requisiti relativi alla UI, alla posizione del file e

alle azioni da intraprendere nel caso in cui il contenuto del file non sia valido.)

TimeRange 4 di 30

2 Un approccio procedurale

Davanti a un problema simile la prima questione è: come rappresentare i dati. Naturalmente, il

modello scelto deve avere la funzione di favorire l'implementazione delle richieste del problema:

visualizzazione delle informazioni sugli atleti ed elaborazioni sulla base dei loro tempi.

Di seguito fornisco alcune opzioni, delle quali le prime due sono volutamente inadeguate. (Ma

che evidenziano l'importanza di fornire un'appropriata rappresentazione dei dati.)

2.1 Opzione 1: nessun modello

È la scelta più semplice: ogni atleta è rappresentato da una stringa che memorizza la riga corri-

spondente del file.

class Program

{ static void Main(string[] args)

{ string[] atleti = CaricaAtleti("Maratona.txt");

//... elabora i dati

}

static string[] CaricaAtleti(string nomeFile)

{ return File.ReadAllLines(nomeFile);

}

}

Il codice di caricamento è semplicissimo; d'altra parte, avere le informazioni nella forma:

" , , "

Tadesse Abraham Svizzera 2:11:42

rende complicata l'implementazione dei requisiti, poiché implica l'estrazione dei dati (nominativo,

nazione e tempo) per ogni operazione da realizzare.

2.2 Opzione 2: utilizzare dei campi stringa

Ogni atleta è rappresentato da tre campi stringa; i dati degli atleti sono memorizzati in una matri-

ce di tre colonne.

static string[,] CaricaAtleti(string nomeFile)

{ string[] righe = File.ReadAllLines(nomeFile);

string[,] atleti = new string[righe.Length, 3];

for (int i = 0; i < righe.Length; i++)

{ string[] campi = righe[i].Split(',');

atleti[i, 0] = campi[0].Trim();

atleti[i, 1] = campi[1].Trim();

atleti[i, 2] = campi[2].Trim();

}

return atleti;

}

TimeRange 5 di 30

I dati di un atleta sono memorizzati separatamente, ma la loro elaborazione non risulta molto più

semplice rispetto a prima: ogni dato è individuato attraverso una coppia di coordinate nella

matrice. Inoltre, i tempi sono memorizzati come stringhe e dunque non è possibile utilizzarli

direttamente nelle operazioni necessarie a soddisfare i requisiti del problema.

2.3 Opzione 3: record

L'uso di un consente di rappresentare il singolo atleta in modo significativo:

record

class Atleta

{ public string Nominativo;

public string Nazione;

public string Tempo;

}

static Atleta[] CaricaAtleti(string nomeFile)

{ string[] righe = File.ReadAllLines(nomeFile);

Atleta[] atleti = new Atleta[righe.Length];

for (int i = 0; i < righe.Length; i++)

{ string[] campi = righe[i].Split(',');

atleti[i].Nominativo = campi[0].Trim();

atleti[i].Nazione = campi[1].Trim();

atleti[i].Tempo = campi[2].Trim();

}

return atleti;

}

Il tipo incapsula il concetto di e consente di gestire gli atleti come oggetti del tipo

atleta

Atleta

appropriato, mantenendo la possibilità di processare i loro attributi: , e .

Nominativo Nazione Tempo

Per quest'ultimo è tuttora utilizzato il tipo ; ciò non consente di eseguire direttamente

string

operazioni sui tempi. Ad esempio, il tempo di:

"Tadesse Abraham, Svizzera, "

2:11:42

è maggiore del tempo di: "Eliud Kipchoge, Kenya, "

2:8:44

ma così non risulterebbe da un confronto tra stringhe, poiché la prima è minore della seconda.

Le opzioni considerate finora sollevano l’identica questione: la rappresentazione dei dati

poiché sono i tipi a stabilire i vincoli e le operazioni

mediante dei tipi adeguati è fondamentale,

ammissibili su di essi. C# fornisce i tipi primitivi ( , , , , etc), i quali

int char string double

definiscono una rappresentazione di interi, caratteri, stringhe, etc. In alcune situazioni sono

sufficienti, in altre è necessario definire nuovi tipi:

perché non esiste un tipo che possa rappresentare il dato: vedi, ad esempio, i singoli atleti;

• perché i tipi utilizzabili non forniscono le operazione richieste: vedi i tempi degli atleti.

TimeRange 6 di 30

2.4 Rappresentare il tempo degli atleti: intervallo di tempo

Elaborare il tempo degli atleti significa poter conoscere ore, minuti e secondi, confrontare due

tempi, ottenere l'intervallo che intercorre tra due tempi. Un approccio è quello di rappresentare

un singolo tempo mediante un record:

class TimeRange

{ public int Ore;

public int Minuti;

public int Secondi;

}

...

class Atleta

{ public string Nominativo;

public string Nazione;

public TimeRange Tempo;

}

2.4.1 Implementare le operazioni sui tempi

si limita a memorizzare i dati; al codice applicativo va la responsabilità di implementare

TimeRange

le operazioni necessarie:

static TimeRange Parse(string tempo) // ottiene un tempo da una stringa

{ string[] campi = tempo.Split(':');

var t = new TimeRange();

t.Ore = int.Parse(campi[0]);

t.Minuti = int.Parse(campi[1]);

t.Secondi = int.Parse(campi[2]);

return tr;

}

static string ToString(TimeRange t) // converte un tempo in stringa

{ string segno = (t.Ore < 0 || t.Minuti < 0 || t.Secondi < 0) ? "-" : "";

return string.Format("{0}{1}:{2}:{3}", segno,

Math.Abs(t.Ore), Math.Abs(t.Minuti), Math.Abs(t.Secondi));

}

static int TempoTotale(TimeRange t) // ottiene i secondi totali del tempo

{ return t.Ore * 3600 + t.Minuti * 60 + t.Secondi;

}

static TimeRange Intervallo(TimeRange t1, TimeRange t2) // ottiene l'intervallo di tempo

{ // che intercorre tra due tempi

var tr = new TimeRange();

int scarto = TempoTotale(t1) - TempoTotale(t2);

tr.Ore = scarto / 3600 % 3600;

tr.Minuti = scarto / 60 % 60;

TimeRange 7 di 30

tr.Secondi = scarto % 60;

return tr;

}

static int Confronta(TimeRange t1, TimeRange t2) // confronta due tempi e restituisce:

{ // -1, 0, 1 in base al fatto che il primo

int sect1 = TempoTotale(t1); // sia minore, uguale, maggiore del secondo

int sect2 = TempoTotale(t2);

if (sect1 > sect2)

return 1;

if (sect1 < sect2)

return -1;

return 0;

}

2.5 Implementazione dei requisiti

Sulla scorta dei metodi precedenti, si possono implementare i requisiti:

static void Main(string[] args)

{ Atleta[] atleti = CaricaAtleti("Maratona.txt");

Ordina(atleti);

Visualizza(atleti);

Atleta primo = atleti[0];

Atleta ultimo = atleti[atleti.Length - 1];

TimeRange scarto = Intervallo(ultimo.Tempo, primo.Tempo);

Console.WriteLine("\nTempo fra ultimo e primo = {0}", ToString(scarto));

}

static Atleta[] CaricaAtleti(string nomeFile)

{ string[] righe = File.ReadAllLines(nomeFile);

Atleta[] atleti = new Atleta[righe.Length];

for (int i = 0; i < righe.Length; i++)

{ string[] campi = righe[i].Split(',');

atleti[i] = new Atleta();

atleti[i].Nominativo = campi[0].Trim();

atleti[i].Nazione = campi[1].Trim();

atleti[i].Tempo = Parse(campi[2]);

}

return atleti;

}

static void Visualizza(Atleta[] atleti)

{ Console.WriteLine("{0,-25} {1,-15} {2}\n", "Nominativo", "Nazione", "Tempo (h/m/s)");

foreach (var a in atleti)

{

TimeRange 8 di 30

Console.WriteLine("{0,-25} {1,-15} {2}", a.Nominativo, a.Nazione,

ToString(a.Tempo));

}

}

static void Ordina(Atleta[] atleti)

{ for (int i = 0; i < atleti.Length-1; i++)

{ for (int j = i+1; j < atleti.Length; j++)

{ if (Confronta(atleti[i].Tempo, atleti[j].Tempo) > 0)

{ var temp = atleti[i];

atleti[i] = atleti[j];

atleti[j] = temp;

}

}

}

}

2.6 Conclusioni

L’insieme dei metodi , , e , fornisce un supporto

Parse() ToString() Confronta() Intervallo()

all’elaborazione dei tempi degli atleti; si tratta di una soluzione funzionale, ma non ottimale.

Infatti:

1. Non consente di processare i tempi come accade con gli altri valori. Vorremmo poter

scrivere:

if (atleti[i].Tempo > atleti[j].Tempo)

Oppure:

TimeRange scarto = ultimo – primo;

Ma non possiamo farlo, poiché è un semplice e non definisce alcuna

record

TimeRange

operazione.

2. Si tratta di una soluzione difficile da riutilizzare in altri scenari; occorre copiare e

TimeRange

i metodi che lo elaborano nei nuovi progetti. Non esiste, cioè, un componente da poter

facilmente riutilizzare.

3. Si tratta di una soluzione funzionale al problema corrente, ma probabilmente insufficiente

in altri scenari, nei quali potrebbe essere necessario considerare anche i giorni, oppure

sommare i tempi. In questo caso saremmo costretti ad aggiungere le operazioni

necessarie, col risultato di avere soluzioni simili, ma non identiche, al problema di

rappresentare lo stesso concetto: un intervallo di tempo.

TimeRange 9 di 30

3 Un approccio object oriented

La programmazione presuppone che i tipi incorporino i dati e le operazioni da

object oriented

eseguire su di essi. Non si tratta di un approccio sempre desiderabile; lo diventa soprattutto

quando il tipo rappresenta un concetto indipendente da specifiche applicazioni; il concetto di

(intervallo di) tempo rientra senz'altro in questa casistica. Naturalmente, applicazioni distinte

possono avere requisiti diversi. Alcune potrebbero dover misurare il tempo in giorni (un

programma che registra dei promemoria), altre in millisecondi (programmi di Alcune

benchmark).

devono confrontare dei tempi, in altre basta conoscere ore, minuti, secondi, etc. Proprio per

questo motivo è molto utile incorporare queste caratteristiche in un oggetto, in modo da poter:

semplificare la realizzazione di qualunque codice debba gestire dei tempi;

• riutilizzare lo stesso concetto in applicazioni diverse senza dover scrivere del codice ad

• hoc.

3.1 Oggetto TimeRange: dati + operazioni

Se ci si limita a considerare il “mantra” della OOP, combinare dati e operazioni nello stesso

(l'oggetto, appunto), la trasformazione da a è semplice: basta

contenitore, record oggetto

incorporare i metodi di elaborazione dei tempi nel tipo :

TimeRange

class TimeRange

{ public int Ore;

public int Minuti;

public int Secondi;

//static TimeRange Parse (string tempo) {...}

public void Parse(string tempo)

{ string[] campi = tempo.Split(':');

Ore = int.Parse(campi[0]);

Minuti = int.Parse(campi[1]);

Secondi = int.Parse(campi[2]);

}

//static int Confronta(TimeRange t1, TimeRange t2) {...}

public int Confronta(TimeRange t)

{ int sect1 = TempoTotale(this); //-> calcola i secondi di "se stesso"

int sect2 = TempoTotale(t);

if (sect1 > sect2)

return 1;

if (sect1 < sect2)

return -1;

return 0;

}

//static TimeRange Intervallo(TimeRange t1, TimeRange t2) {...}

public TimeRange Intervallo(TimeRange t)

TimeRange 10 di 30

{ int scarto = TempoTotale(this) - TempoTotale(t);

var tr = new TimeRange();

tr.Ore = scarto / 3600 % 3600;

tr.Minuti = scarto / 60 % 60;

tr.Secondi = scarto % 60;

return tr;

}

//static string ToString(TimeRange t) {...}

public string ToString()

Dettagli
Publisher
30 pagine