Counter2

Dette eksemplet utvider Counter1-eksemplet med mulighet for å restarte tellingen.

Objekt-utforming

Som tidligere ønsker vi oss et objekt som holder rede på en heltallsteller, som skal løpe fra en startverdi til en sluttverdi. Nå skal telleren altså kunne restartes (uavhengig av om den er ferdig eller ikke). Vi må stille oss de samme spørsmålene som tidligere:

  • Hva må en kunne spørre objektet om?
    Vi trenger ingen flere lese/spørre-metoder.

  • Hvilke operasjoner må en kunne utføre på dataene?
    Vi trenger en ny endringsoperasjon for å restarte!

  • Hva må objektet huske (på av data) for å kunne oppføre seg riktig?
    I tillegg til teller-verdien og den øvre grensen, så må vi å også huske start-verdien!

  • Hvilke data må oppgis når objektet opprettes/starter?
    Som tidligere, så må en oppgi start-verdien og den øvre grensen.

Koding

Det enkleste er å kopiere koden fra tidligere, og så endre og legge til kode slik at logikken blir riktig.

Klassedeklarasjonen

Siden klassenavnet nå er Counter2 (og det fulle navnet til klassen er stateandbehavior.counter.Counter2), så må klasse-deklarasjonen endres:

package stateandbehavior.counter;

class Counter2 {
   // resten er mye som før
}
Tip
Det enkleste er å bruke kopier og lim inn-funksjonene. Høyreklikk på Counter1.java-fila og velg Copy. Høyreklikk så på stateandbehavior.counter-pakken (mappa) og velg Paste. Siden det allerede ligger en klasse/fil med samme navn, så spør Eclipse om et nytt navn, skriv da inn Counter2. Åpne så den nye Counter2.java-fila og bygg videre på innholdet.

Variabeldeklarasjoner

Vi må legge til en start-variabel, siden vi jo må huske den opprinnelige start-verdien:

	int start; // (1)
	int end; // (2)
	int counter; // (2)
  1. ny

  2. som før

Konstruktør(er) og metoder

Konstruktøren må endres og metoden for å restarte legges til:

	Counter2(int start, int end) { // (1)
		this.start = start;
		this.end = end;
		restart();
	}
	void restart() { // (2)
		this.counter = start;
	}
  1. noe endret

  2. ny

Her har vi valgt å kalle restart-metoden i konstruktøren, siden det som gjenstår etter initialisering av start- og end-variablene nødvendigvis tilsvarer å (re)starte telleren.

Objektdiagram

Objektdiagrammet for et objekt opprettet med new Counter2(2, 5) vil se ut som dette:

diag 5b622573ecb6c5550c1073dff97cf007

main-metode og objekttilstandsdiagram

En mulig main-metode for å teste Counter2-klassen er vist under.

	public static void main(String[] args) {
		Counter2 counter = new Counter2(2, 3); // (1)
		System.out.println("Counter is: " + counter.getCounter()); // (2)
		System.out.println("isFinished? " + counter.isFinished()); // (2)
		counter.count(); // (3)
		System.out.println("Counter is: " + counter.getCounter()); // (2)
		System.out.println("isFinished? " + counter.isFinished()); // (2)
		counter.restart(); // (3)
		System.out.println("Counter is: " + counter.getCounter()); // (2)
		System.out.println("isFinished? " + counter.isFinished()); // (2)
		counter.count(); // (3)
		System.out.println("Counter is: " + counter.getCounter()); // (2)
		System.out.println("isFinished? " + counter.isFinished()); // (2)
	}
  1. Testobjekt opprettes.

  2. Tilstand hentes ut med lesemetode og skrives ut.

  3. Endringsmetode kalles og tilstand endres (potensielt).

Endringene i tilstand kan illustreres med følgende objekttilstandsdiagram:

diag 18c0ee60a13d15acad47b62f267c6728

Merk hvordan kallet til restart leder tilbake til den første tilstanden. Det er jo hele hensikten med restart-metoden, å sette tilstanden tilbake til slik den var i starten!

Alternativ utforming og kode

Koden over skal virke, men kunne det vært gjort på en annen måte? En variant er å la counter-variablen løpe fra 0 og oppover. Da må følgende metoder endres:

int getCounter() {
   return start + counter;
}

boolean isFinished() {
   return getCounter() >= end;
}

void restart() {
   counter = 0;
}

Merk hvor isFinished før brukte counter-variablen direkte, så går vi nå over til å kalle getCounter. Da er vi sikker å at vi sammenligner riktig verdi med end, uavhengig hvordan getCounter beregner verdien.

Objektdiagrammet for et objekt som akkurat er opprettet med new Counter2(2, 5) vil med denne alternative utformingen, se slik ut:

diag 5b18fa5bb0014bf977d584a4ea72aec8

Du kan selv prøve å tegne objekttilstandsdiagram tilsvarende det over, men som stemmer med den nye utformingen. Hint: Siden det jo er meningen at de to alternative utformingene skal gi samme logiske oppførsel, så vil strukturen til diagrammet være som før.

De to variantene er like gode, så det er ikke noe poeng i seg selv å endre koden på denne måten, men det illustrerer to viktige poeng:

  1. Kode som bare bruker Counter2-objektene sine metoder, vil ikke merke endringen! Vi kan si at endringene kun er interne for Counter2-klassen.

  2. Hvis vi kaller metoder fra inni klassen, slik konstruktøren kaller restart og isFinished kaller getCounter, heller enn å bruke variabler direkte, så vil endring av logikken kreve færre endringer totalt sett.

Disse to poengene har med abstraksjon og innkapsling å gjøre, to veldig viktige begreper innen objektorientert programmering!