Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

TimeInterval-klassen skal representere et del av en dag, f.eks. en forelesning eller møtetidsrom til bruk i en avtalekalender. Klassen inneholder to tidspunkt, begge representert med to heltall for timen og minuttet. Merk at det finnes flere måter å representere dette på, f.eks. representere tidspunktene som antall minutter siden midnatt (gir enklere logikk) eller intervallet med start-tidspunkt og lengde i minutter. Disse alternativene diskuterer vi ikke her.

Her er variablene deklarert i en klassedefinisjon:

Code Block
languagejava
public class TimeInterval {
	int startHour

...

; // timen intervallet starter, med 0 som første time etter midnatt

...


	int startMin

...

; // minuttet innen start-timen som intervallet starter 

...


	int endHour

...

; // timen intervallet slutter

...


	int endMin

...

; // minuttet innen slutt-timen, som intervallet slutter 
}

En liten detalj må avklares: Er slutt-tidspunktet med i intervallet eller ikke? Hvis vi f.eks. setter endHour til 16 og endMin til 0, betyr det at forelesningen er ferdig til kl. 16:00 eller at vi bruker det første minuttet av 16-timen? Vi velger den første tolkningen, så slutt-tidspunktet er først minutt som ikke er med i intervallet. Dette betyr noe for hva som er gyldig tilstand, som vi skal se.

...

Det finnes ingen klare regler for hvilke metoder en klasse må ha, men det er ikke uvanlig å (vurdere å) ha såkalte gettere og settere for hvert felt. For TimeInterval-klassen betyr det å muligheten til å sette hver variablen for seg, altså int getStartHour() og void setStartHour(int hour) for å lese og sette startHour, int getStartMin() og void setStartMin(int min) for å lese og sette startMin osv. for endHour og endMin. Koden for et get/set-par (uten validering) blir som følgervist under til venstre:

Code Block
languagejava

...

public int getStartHour() {

...

	return startHour;
}
    

...


public void setStartHour(int startHour) {
    this.startHour = startHour;
}
Code Block
languagejava
public void 

...

setStart(int startHour, int startMin) {

...

    this.startHour = startHour;
    this.startMin = startMin;
}

Nå kan man spørre seg om det ikke er mest praktisk å kunne sette mer enn én verdi om gangen, f.eks. sette start-tidspunktet, dvs. startHour og startMin, med én metpder metode og tilsvarende for slutt-tidspunktet. Dette gir følgende set-metode:

Code Block
languagejava
    public void setStart(int startHour, int startMin) {
        this.startHour = startHour;
        this.startMin = startMin;
    }

 

 

. Denne varianten er vist over til høyre. I praksis spiller det ikke så mye rolle for hvordan vi håndterer validering, som vi nå skal se på.

Validering

Hensikten med å lage setter-metoder er ikke først og fremst å gjøre det praktisk å sette tilstand, men å kunne sikre gyldig tilstand ved å legge til valideringskode som er garantert å alltid blir utført. Den enkleste varianten er å legge tilordningen inni en if, og ha en betingelse som sikrer at tilordningen kun blir utført om den nye verdien er gyldig, som vist til venstre under. En annen variant er å ha en if med den motsatte betingelsen, altså om den nye verdien er ugyldig, og utløse et passende unntak, som vist under til høyre.

Code Block
languagejava
public void setStartHour(int startHour) {
	if (startHour >= 0 && startHour < 24) {
		this.startHour = startHour;
	}
}
Code Block
languagejava
public void setStartHour(int startHour) {
	if (startHour < 0 || startHour >= 24) {
		throw new IllegalArgumentException("An hour must be >= 0 and < 24, but was " + startHour);
	}
	this.startHour = startHour;
}

Forskjellen mellom disse virker ikke så stor, men det er nokså vesentlig å vite om ugyldig argument gir unntak som kræsjer med programmet, eller ikke! Varianten til høyre er generelt å anbefale, fordi å stiltiende ignorere ugyldige argumenter vil bare utsette problemet til siden og gjøre kilden til problemet vanskeligere å finne.

Siden vi i TimeInterval-klassen har to sett med like variabler, så vil det være en fordel å flytte valideringskoden over i egne metoder, som kalles fra setter-metodene. En har typisk to typer metoder, de som returnerer en boolean verdi som angir om verdier er gyldige og de som både sjekker gyldighet og utløser unntak hvis så ikke er tilfelle. Disse variantene er vist under hhv. til venstre og høyre. Merk at teknikken er den samme om en har setter-metoder for flere verdier på en gang, som setStart-metoden over. En har likevel valideringsmetoder for hver enkeltverdi og kaller dem begge.

Code Block
languagejava
private boolean isValidHour(int hour) {
	return hour >= 0 && hour < 24;
}

public void setStartHour(int startHour) {
	if (isValidHour(startHour)) {
		this.startHour = startHour;
	}
}
Code Block
languagejava
private void checkHour(int hour) {
	if (hour < 0 || hour >= 24) {
		throw new IllegalArgumentException("An hour must be >= 0 and < 24, but was " + hour);
	}
}

public void setStartHour(int startHour) {
	checkHour(startHour);
	this.startHour = startHour;
}

Uansett hvilken variant en velger, så kan det være greit å ha en public boolean isValid...-metode, slik at andre klasser selv kan sjekke om verdier er gyldige før de evt. kaller metoden. Tenk f.eks. at en bruker fyller inn et skjema for et slik tidsinterval med et innfyllingsfelt for hver verdi. Med en offentlig valideringsmetode kan en sjekke gyldighet og markere innfyllingsfelt som ugyldige, uten å først kalle setter-metoden og i etterkant sjekke om det gikk.

Validering av gyldighet på tvers av felt

Koden over sjekke ikke om TimeInterval-objektet er gyldig som helhet, altså om slutt-tidspunktet er det samme som eller etter start-tidspunktet. Teknikken er generelt den samme: En lager egne metoder for hver regel. Forskjellen er at en må kalle den fra alle setter-metodene, siden alle potensielt kan bryte regelen for gyldig tilstand. Koden er vist under, basert på varianten med check...-metoder. Vi har her tatt med alle setter-metodene, for å nettopp vise at alle disse må forholde seg til regler for både enkeltverdier og totaltilstanden.

Code Block
languagejava
private void checkHour(int hour) {
	if (hour < 0 || hour >= 24) {
		throw new IllegalArgumentException("An hour must be >= 0 and < 24, but was " + hour);
	}
}

private checkMin(int min) {
	if (min < 0 || min >= 60) {
		throw new IllegalArgumentException("A minute must be >= 0 and < 60, but was " + hour);
	}
}

private checkStartLessThanOrEqualToEnd(int startHour, int startMin, int endHour, int endMin) {
	if (endHour < startHour || (endHour == startHour && endMin < startMin)) {
		throw new IllegalArgumentException("End cannot be less than start");
	}
}

public void setStartHour(int startHour) {
	checkHour(startHour);
	checkStartLessThanOrEqualToEnd(startHour, this.startMin, this.endHour, this.endMin);
	this.startHour = startHour;
}

public void setStartMin(int startMin) {
	checkMin(startMin);
	checkStartLessThanOrEqualToEnd(this.startHour, startMin, this.endHour, this.endMin);
	this.startMin = startMin;
}

public void setEndHour(int endHour) {
	checkHour(endHour);
	checkStartLessThanOrEqualToEnd(this.startHour, this.startMin, endHour, this.endMin);
	this.endHour = endHour;
}

public void setEndMin(int endMin) {
	checkMin(endMin);
	checkStartLessThanOrEqualToEnd(this.startHour, this.startMin, this.endHour, endMin);
	this.endMin = endMin;
}

Det er viktig å merke seg hvilke verdier som gis som argument til checkStartLessThanOrEqualToEnd-metoden. I hver setter-metode gis den nye verdien som ønskes satt sammen med de andre og eksisterende felt-verdiene. Dette gjøres ekstra tydelig ved bruk av this foran feltene, selv om det det strengt tatt ikke er nødvendig.