...
Code Block | ||
---|---|---|
| ||
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 på int endHour; // timen intervallet slutter int endMin; // minuttet innen slutt-timen, som intervallet slutter på } @Override public String toString() { // formats each part as a two-digit number with a leading zero return String.format("%02d:%02d-%02d:%02d", startHour, startMin, endHour, endMin); } } |
Vi har her lagt til en toString()-metode, for å gjøre det enklere å teste koden med System.out.println(...). String.format-metoden gjør det enkelt å bygge opp en String med strukturert innhold. Merk spesielt hvordan formatteringsdirektivet %02d angi tre ting på en gang: d-en betyr at vi formatterer et tall, 2-tallet at det skal settes av plass til to siffer og 0-tallet at det skal fylles på med 0-er (og ikke mellomrom) dersom tallet er på kun ett siffer.
En liten detalj må forøvrig 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 angir første minutt som ikke er med i intervallet. Dette 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.
...
Innkapsling er teknikken for å sikre tilstand og består av 1) synlighetsmodifikatorer og 2) innkapslingsmetoder.
Synlighetsmodifikatorer
Synlighetsmodifikatorer begrenser hvilke variabler (felt) og metoder som andre klasser får bruke. Den generelle regelen for å sikre gyldig tilstand er at alle felt skal være private og dermed utilgjengelig for andre klasser, mens innkapslingsmetodene skal være offentlige (public). Grunnen er jo at offentlige felt ikke kan beskyttes med kode som validerer verdier. Vi kan forøvrig også ha private metoder, f.eks. hjelpemetoder, som ikke er garantert å sikre gyldig tilstand.
...
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.. 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 | ||
---|---|---|
| ||
private void checkHour(int hour) {
if (hour < 0 || hour >= 24) {
throw new IllegalArgumentException("An hour must be >= 0 and < 24, but was " + hour);
}
}
private void checkMin(int min) {
if (min < 0 || min >= 60) {
throw new IllegalArgumentException("A minute must be >= 0 and < 60, but was " + min);
}
}
private void 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.
Her er to varianter av hele koden, den første er omtrent tilsvarende koden over (små forskjeller, fordi det forløp litt annerledes på forelesningen) og den andre utvider klassen med en konstruktør og to metoder som lar deg sette hhv. start- og slutt-tidspunktene samlet. De to variantene er navngitt TimeInterval1 og TimeInterval2 i pakken encapsulation slik at de er greie å importere.
Code Block | ||||
---|---|---|---|---|
| ||||
package encapsulation;
public class TimeInterval1 {
private int startHour; // >= 0 og < 24
private int startMin; // >= 0 og < 60
private int endHour; // være etter start
private int endMin; //
@Override
public String toString() {
// lager en String basert på en mal som fylles med verdier fra argumentene
return String.format("%02d:%02d-%02d:%02d", startHour, startMin, endHour, endMin);
}
// sjekker om slutt-tidspunktet er etter eller lik start-tidspunktet
// hvis så ikke er tilfelle, så utløses unntak
private void checkEndTimeIsLaterOrEqualToStartTime(int startHour, int startMin, int endHour, int endMin) {
if (endHour < startHour || (endHour == startHour && endMin < startMin)) {
throw new IllegalArgumentException("Slutt må være etter eller lik start");
}
}
// returnerer startHour slik at andre klasser kan lese tilstanden
public int getStartHour() {
return startHour;
}
// returnerer om hour er en gyldig time
public boolean isValidHour(int hour) {
return (hour >= 0 && hour < 24);
}
// utløser unntak hvis hour ikke er gyldig
private void checkHour(int hour) {
if (! isValidHour(hour)) {
throw new IllegalArgumentException("En time kan ikke være mindre enn 0 eller større eller lik 24");
}
}
// setter startHour, etter først å ha sjekke gyldighet av startHour isolert og sammen med de andre verdiene
public void setStartHour(int startHour) {
checkHour(startHour);
checkEndTimeIsLaterOrEqualToStartTime(startHour, this.startMin, this.endHour, this.endMin);
this.startHour = startHour;
}
// returnerer startMin slik at andre klasser kan lese tilstanden
public int getStartMin() {
return startMin;
}
// returnerer om min er et gyldig minutt
public boolean isValidMin(int min) {
return (min >= 0 && min < 60);
}
// utløser unntak hvis min ikke er gyldig
private void checkMin(int min) {
if (! isValidMin(min)) {
throw new IllegalArgumentException("Et minutt kan ikke være mindre enn 0 eller større eller lik 60");
}
}
// setter startMin, etter først å ha sjekke gyldighet av startMin isolert og sammen med de andre verdiene
public void setStartMin(int startMin) {
checkMin(startMin);
checkEndTimeIsLaterOrEqualToStartTime(this.startHour, startMin, this.endHour, this.endMin);
this.startMin = startMin;
}
// returnerer endHour slik at andre klasser kan lese tilstanden
public int getEndHour() {
return endHour;
}
// setter endHour, etter først å ha sjekke gyldighet av endHour isolert og sammen med de andre verdiene
public void setEndHour(int endHour) {
checkHour(endHour);
checkEndTimeIsLaterOrEqualToStartTime(this.startHour, this.startMin, endHour, this.endMin);
this.endHour = endHour;
}
// returnerer endMin slik at andre klasser kan lese tilstanden
public int getEndMin() {
return endMin;
}
// setter endMin, etter først å ha sjekke gyldighet av endMin isolert og sammen med de andre verdiene
public void setEndMin(int endMin) {
checkMin(endMin);
checkEndTimeIsLaterOrEqualToStartTime(this.startHour, this.startMin, this.endHour, endMin);
this.endMin = endMin;
}
} |
Code Block | ||||
---|---|---|---|---|
| ||||
package encapsulation;
public class TimeInterval2 {
private int startHour; // >= 0 og < 24
private int startMin; // >= 0 og < 60
private int endHour; // være etter start
private int endMin; //
// initialiserer objektet, basert på argumentene
// sjekker først gyldigheten av alle verdiene enkeltvis og så samlet, før feltene settes
public TimeInterval2(int startHour, int startMin, int endHour, int endMin) {
checkHour(startHour);
checkMin(startMin);
checkHour(endHour);
checkMin(endMin);
checkEndTimeIsLaterOrEqualToStartTime(startHour, startMin, endHour, endMin);
this.startHour = startHour;
this.startMin = startMin;
this.endHour = endHour;
this.endMin = endMin;
}
// setter start-tidspunktet
// sjekker først gyldigheten av verdiene enkeltvis og så samlet, før feltene settes
public void setStartTime(int startHour, int startMin) {
checkHour(startHour);
checkMin(startMin);
checkEndTimeIsLaterOrEqualToStartTime(startHour, startMin, this.endHour, this.endMin);
this.startHour = startHour;
this.startMin = startMin;
}
// setter slutt-tidspunktet
// sjekker først gyldigheten av verdiene enkeltvis og så samlet, før feltene settes
public void setEndTime(int endHour, int endMin) {
checkHour(endHour);
checkMin(endMin);
checkEndTimeIsLaterOrEqualToStartTime(this.startHour, this.startMin, endHour, endMin);
this.endHour = endHour;
this.endMin = endMin;
}
@Override
public String toString() {
return String.format("%02d:%02d-%02d:%02d", startHour, startMin, endHour, endMin);
}
private void checkEndTimeIsLaterOrEqualToStartTime(int startHour, int startMin, int endHour, int endMin) {
if (endHour < startHour || (endHour == startHour && endMin < startMin)) {
throw new IllegalArgumentException("Slutt må være etter eller lik start");
}
}
public int getStartHour() {
return startHour;
}
public boolean isValidHour(int hour) {
return (hour >= 0 && hour < 24);
}
private void checkHour(int hour) {
if (! isValidHour(hour)) {
throw new IllegalArgumentException("En time kan ikke være mindre enn 0 eller større eller lik 24");
}
}
public void setStartHour(int startHour) {
checkHour(startHour);
checkEndTimeIsLaterOrEqualToStartTime(startHour, this.startMin, this.endHour, this.endMin);
this.startHour = startHour;
}
public int getStartMin() {
return startMin;
}
public boolean isValidMin(int min) {
return (min >= 0 && min < 60);
}
private void | ||||
Code Block | ||||
| ||||
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) { if (! isValidMin(min)) { throw new IllegalArgumentException("A minute must be >=Et minutt kan ikke være mindre enn 0 andeller <større 60,eller butlik was " + hour60"); } } 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. } } public void setStartMin(int startMin) { checkMin(startMin); checkEndTimeIsLaterOrEqualToStartTime(this.startHour, startMin, this.endHour, this.endMin); this.startHour = startHour; } startMin = startMin; } public int getEndHour() { return endHour; } public void setStartMinsetEndHour(int startMinendHour) { checkMin(startMin checkHour(endHour); checkStartLessThanOrEqualToEnd checkEndTimeIsLaterOrEqualToStartTime(this.startHour, this.startMin, this.endHour, this.endMin); this.startMinendHour = startMinendHour; } public void setEndHour(int endHourgetEndMin() { checkHour(endHour); checkStartLessThanOrEqualToEnd(this.startHour, this.startMin, endHour, this.endMin); this.endHour = endHour; } return endMin; } public void setEndMin(int endMin) { checkMin(endMin); checkStartLessThanOrEqualToEnd(this.startHour, this.startMin, this.endHour, checkMin(endMin); this.endMin = endMin; } checkEndTimeIsLaterOrEqualToStartTime(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.