Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 5.3

...

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 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
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 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
languagejava
titleTimeInterval1
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
languagejava
titleTimeInterval2
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
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) {
		        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.