Et objekt har både tilstand og oppførsel. Tilstanden til et objekt er alle dataene (variabler og verdier) det inneholder, mens oppførselen er hva objektet kan gjøre av beregninger og operasjoner på dataene og hvordan disse potensielt endrer tilstanden over tid.

Ta som eksempel et Counter-objekt som teller fra en start-verdi til en slutt-verdi. Slutt-verdien angis når objektet opprettes og kan variere fra objekt til objekt. Objektet må altså huske både teller-verdien og slutt-verdien (begge er heltall), så disse utgjør objektets tilstand. Objektet har to operasjoner, int getCounter(), som returnerer nåværende teller-verdi, og void count(), som øker telleren med 1 dersom den ikke har nådd slutt-verdien. Oppførselen defineres av disse to operasjonene, først og fremst ved hvordan count evt. endrer tilstanden, men også hvordan tilstanden leses av getCounter og dermed påvirkes av count.

En måte å illustrere oppførselen på er et diagram over hvordan tilstanden til objektet utvikler seg over tid, når ulike metoder blir kalt.

#1:Countercounter = 1end = 3#1:Countercounter = 2end = 3#1:Countercounter = 3end = 3getCounter() => 1count()getCounter() => 2count()getCounter() => 3count()

Figuren til venstre viser hvordan samme Counter-objekt går fra én tilstand (verdiene til counter og end) til en annen, når getCounter- og count-operasjonene utføres. Hver boks er altså det samme objektet i ulike tilstander og pilene angir hvilket kall som leder til neste (eller samme) tilstand. Kallene til getCounter endrer ikke noen verdier, så derfor leder de til samme tilstand. Kallene til count, derimot, endrer telleren (counter) inntil den når slutt-verdien (end) og gir en (heldigvis endelig) kjede med tilstander (se fotnote 1).

Til høyre ser du kode som implementerer denne oppførselen.

public class Counter {
	int end;
	int counter = 0;


	Counter(int end) {
		this.end = end;
	}

	int getCounter() {
		return counter;
	}

	void count() {
		if (counter < end) {
			counter = counter + 1;
		}
	}
 }

Generelt er tilstanden til et objekt verdien av alle attributtene, mens oppførselen er når en kan kalle de ulike operasjonene, hvilke argumenter som er gyldige og hva de returnerer og hvordan tilstanden endres av dem. I eksemplet er det alltid tillatt å kalle begge operasjonene, men en kunne tenke seg at count tok et argument som anga hvor mye telleren skulle økes og at det var ulovlig (å prøve) å øke telleren forbi slutt-verdien (for enkelhets skyld antar vi at både end og inc er positive tall). Dette gir en annen oppførsel, som er forsøkt illustrert i diagrammet under.

#2:Counter2counter = 1end = 3#2:Counter2counter = 2end = 3#2:Counter2counter = 3end = 3count(1)count(2)count(1)count(2)

Vi ser at ett kall til count(2) har samme effekt som to kall til count(1) og at det ikke lenger er lov å kalle count i den siste tilstanden, dvs. når telleren (counter) har nådd slutt-verdien (end). Kall til getCounter er utelatt, siden de (fortsatt) ikke endrer tilstanden.

I koden til høyre er count-metoden endret til å ta inn et int-argument, som angir hvor mye telleren skal øke. Hvis grensen allerede er nådd, så utløses et IllegalStateException-unntak, som er en måte å si at det er ulovlig å kalle count-metoden gitt tilstanden til objektet. Hvis unntaket ikke utløses, så økes telleren og settes så tilbake hvis den er for høy.

public class Counter2 {
	int end;
	int counter = 0;


	Counter2(int end) {
		this.end = end;
	}

	int getCounter() {
		return counter;
	}

	void count(int inc) {
		if (counter >= end) {
			throw new IllegalStateException("Cannot increment counter, when limit has been reached");
		}
		counter = counter + inc;
		if (counter >= end) {
			counter = end;
		}
	}
 }

For den som bruker et objekt (eller klasse) er det viktig å vite hvordan et objekt oppfører seg, både for å vite om objektet passer til problemet og hvordan unngå å bruke det feil. For den som skal kode oppførselen i en klasse, er det selvsagt helt vesentlig å tenke gjennom hvilken oppførsel objektene skal ha, og eksempler som de i figurene er ofte nyttige, både for å tenke gjennom problemet før en koder og for å forklare andre hvordan koden virker (eller er ment å virke). Slike eksempler hjelper en også å skrive tester, som forklart i Oppførsel og enhetstesting.

Spørsmål til refleksjon

  1. I kommentaren til det andre diagrammet står det at ett kall til count(2) har samme effekt som to kall til count(1). Dette er imidlertid ikke riktig i alle tilfeller, ser du i hvilket?
  2. Anta at det finnes en metode kalt setEnd(int) som lar en endre slutt-verdien. Hva synes du bør skje hvis slutt-verdien settes lavere enn telleren?

 


1) For så enkel oppførsel som denne går det greit å lage et komplett diagram, men generelt kan det bli veldig mange tilstander (noen ganger uendelig) og veldig mange kombinasjoner/rekkefølger av kall. Derfor er et slikt diagram greit som illustrasjon på et eksempel, men ikke som en komplett beskrivelse av oppførselen.