Versions Compared

Key

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

...

Excerpt

Denne oppgaven handler om å bruke observatør-observert-teknikken for å bli informert om endringer i en HighscoreList-instanshighscore-liste. Vi bruker også arv for å skille ut gjenbrukbar kode for en generell, observerbar liste.

Observatør-observert-teknikken går ut på at det observerte objektet sier ifra til en eller flere observatører om at tilstanden er endret. I denne oppgaven skal vi lage en HighscoreList som kan si fra til lyttere av typen ListListener når nye resultater blir registrert. En hovedprogramklasse kalt HighscoreProgram vil bli brukt til å sjekke at det virker. Denne klassen oppretter en HighscoreList-instans, legger inn resultater (tall) fra konsollet som legges til lista og skriver ut lista hver gang et nytt resultat faktisk blir lagt til.

ListListener-grensesnittet er definert som følger:

Code Block
languagejava
public interface ListListener {
    public void listChanged(ObservableList list, int pos);
}

ListListener-grensesnittet må implementers av alle klasser som ønsker å fungere som lyttere for HighscoreList-instanser. Disse registrere seg med HighscoreList sin addListListener-metode og vil siden få beskjed ved at listChanged-metoden kalles hver gang et nytt resultat kommer inn på lista. Argumentene som tas inn er HighscoreList-objektet som ble endret og posisjonen i lista der endringen skjedde.

 

Klassene skal legges i src/patterns.observable/ og tilhørende ex-fil og tester ligger i tests/patterns.observable/.

Del 1: Implementasjon av

...

PlantUML Macro
class ObservableList {
}

class HighscoreList {
	int maxSize
	Collection<Integer> results
	HighscoreList(int)
	int size()
	int getElement(int)
	void addResult(int)
	addListListener(ListListener)
	removeListListener(ListListener)
}

ObservableList <|-- HighscoreList

interface ListListener {
	listChanged(ObservableList, int)
}
ObservableList -right-> "*" ListListener: listListeners

...

ObservableList og ObservableHighscoreList

En ObservableHighscoreList skal holde styr på heltallsresultater (av typen int/Integer). Lista skal være observerbar ved at den kan registrere lyttere (

...

ObservableListListener-instanser) og si fra til dem når lista blir endret. Lista skal ha en maksimal lengde, som settes i konstruktøren, f.eks. skal en topp 10-liste kunne opprettes med new ObservableHighscoreList(10). Nye resultater registreres med metoden addResult(int), som skal finne riktig posisjon og legge resultatet inn (dersom det er godt nok). Dersom lista er for lang, så skal det dårligste resultatet fjernes. NB: Lavest verdi er best, f.eks. antall sekunder på en oppgave eller antall flytt i Sokoban.

ObservableListListener-grensesnittet er vist i klassediagrammet nedenfor og må implementers av alle klasser som ønsker å fungere som lyttere for ObservableHighscoreList-instanser. Lyttere registrerer seg med ObservableHighscoreList sin addObservableListListener-metode og vil siden få beskjed om nye resultater ved at listChanged-metoden kalles. Argumentene som tas inn er ObservableHighscoreList-objektet som ble endret og posisjonen i lista der endringen skjedde.

Merk at første argument til listChanged-metoden er av typen ObservableList. Dette er en abstrakt superklasse for ObservableHighscoreList

...

, som

...

først brukes i del 3 og som da skal holde orden på lista. ObservableList vil ha en del generelle metoder som ObservableHighscoreList arver og kan bruke. For å kunne kjøre testene for ObservableHighscoreList allerede i del 1, så må ObservableList være definert fra starten. Lag derfor en tom ObservableList-klasse og bruk denne som superklasse for ObservableHighscoreList.

...

Her er en oversikt over metoden som må implementeres:

  • ObservableHighscoreList(int maxSize) - konstruktøren tar inn maks antall resultater som lista skal kunne holde. Denne verdien må brukes av addResult, slik at resultater som er for dårlige kastes.
  • size() - returnerer antall elementer i lista, som altså aldri skal overstige maks-antallet
  • int getElement(int) - returnerer resultatet i posisjonen angitt av argumentet
  • void addResult(int) - registrere et nytt resultat, og dersom resultatet er godt nok til å komme med på lista, så legges det inn på riktig plass. Dersom lista blir for lang, så må dårligste resultat kastes. Alle registrerte lyttere må få beskjed om en evt. endring av lista, inkludert på hvilken posisjon som ble endret.

...

  • addObservableListListener(ObservableListListener) - registrerer en ny lytter

...

  • removeObservableListListener(ObservableListListener) - fjerner en tidligere registrert lytter

Klassediagram for HighscoreList, ListListener og ObservableList:

 

 

PlantUML Macro
class ObservableList {
}

class ObservableHighscoreList {
	int maxSize
	Collection<Integer> results
	ObservableHighscoreList(int)
	int size()
	int getElement(int)
	void addResult(int)
	addObservableListListener(ListListener)
	removeObservableListListener(ListListener)
}

ObservableList <|-- ObservableHighscoreList

interface ObservableListListener {
	listChanged(ObservableList, int)
}
ObservableHighscoreList -right-> "*" ObservableListListener: observableListListeners


Testkode JExercise-testkode for denne oppgaven finner du her: inheritance/HighscoreListTest.java (legg til inheritance/TestListener.java i samme package som HighscoreList. Husk å legge inn ListListener-grensesnittet under før du kjører testen). patterns/observable/ObservableHighscoreListTest.java. Original-koden (jextest) finner du her: inheritancepatterns/observable/HighscoreListObservableHighscoreList.jextest.

Del 2: Hovedprogram

...

Et HighscoreProgram inneholder en HighscoreList med én ListListenerHighscoreProgram. Klassen må derfor implementere grensesnittet ListListener:

Code Block
languagejava
public interface ListListener {
    public void listChanged(AbstractObservableList list, int start, int end);
}

ObservableHighscoreListProgram

Lag en hovedprogramklasse kalt ObservableHighscoreListProgram, som tester at ObservableHighscoreList-klassen din virker som den skal. La den opprette en ObservableHighscoreList-instans, lese inn tall fra konsollet (f.eks. med en Scanner og nextInt-metoden) og legge disse inn i lista. Sørg for at ObservableHighscoreListProgram implementerer ObservableListListener-grensesnittet og registrerer seg som lytter på HighscoreList-instansen. La lyttermetoden listChanged skrive ut informasjon og resultatene i HighscoreList-instansen og posisjonsargumentet, slik at du ser at alt virker som det skal.

Vi foreslår følgende metoder og oppførselKlassen har følgende metoder:

  • void init() - oppretter en ny ObservableHighscoreList og legger til registrerer seg selv som ny ListListener.(altså ObservableHighscoreListProgram-instansen) som lytter
  • void run() - leser inn tall (resultater) fra konsollen konsollet og legger dem til i listen.
  • void listChanged(AbstractObservableList, intObservableList, int) - observerer endringen i listen endringer i ObservableHighscoreList-instansen og skriver ut endringene i listenposisjonsargumentet, samt selve listen, til konsollen.konsollet

Klassediagrammet viser hvordan klassene henger sammen, og vårt forslag til metoder:

PlantUML Macro
class ObservableList {
}

class ObservableHighscoreList {
	int maxSize
	Collection<Integer> results
	ObservableHighscoreList(int)
	int size()
	int getElement(int)
	void addResult(int)
	addObservableListListener(ObservableListListener)
	removeObservableListListener(ObservableListListener)
}

ObservableList <|-- ObservableHighscoreList

interface ObservableListListener {
	listChanged(ObservableList, int)
}
ObservableHighscoreList -right-> "*" ObservableListListener: observableListListeners

class ObservableHighscoreListProgram {
	ObservableHighscoreList observableHighscoreList
	void init()
	void run()
}
ObservableListListener <|.. ObservableHighscoreListProgram


Del 3:

...

ObservableList

Den abstrakte superklassen AbstractObservableList superklassen ObservableList skal legges til som en generell superklasse for observerbare lister, som ObservableHighscoreList skal arve fra. Denne klassen skal både holde oversikt over alle ListListeners som følger en klasse og alle objektene i listen (den observerbare listen). Det innebærer at AbstractObservableList må håndtere endringer i observatører og objekter (dermed bør noe av funksjonaliteten til HighscoreList flyttes opp til superklassen). Klassen skal også implementere grensenittet Iterable<Comparable>, som gjør at man kan iterere over resultatene i den observerbare listen.

AbstractObservableList har i tillegg metoder for å legge og fjerne elementer i den observerbare listen:

en liste med objekter (Object) og håndtere registrering av lyttere, altså en liste med ObservableListListener-instanse, som får beskjed om endringer i lista (slik at lista dermed er observerbar). Dette betyr at ObservableList overtar håndtering av både resultater og lyttere fra ObservableHighscoreList-klassen. For å gjøre ObservableList mer generell og gjenbrukbar, så lar vi den håndtere Object-instanser (heller enn Integer). Samtidig deklarerer den en abstrakt metode acceptsElement, som subklasser må redefinere for å bestemme hva slags objekter det skal være lov å legge inn. ObservableHighscoreList vil f.eks måtte redefinere den slik at bare Integer-objekter aksepteres.

ObservableList skal ha følgende metoder (noen er altså overtatt fra ObservableHighscoreList):

  • int size() - returnerer antall elementer i lista
  • Object getElement(int) - returnerer elementet i posisjonen angitt av argumentet
  • List getList() - returnerer den observerbare listen.
  • int size() - returnerer størrelsen på den observerbare listen.
  • abstract boolean acceptsElement(Object) - returnerer hvorvidt subklassen aksepterer et objekt i den observerbare listen at objektet legges inn i lista (f.eks. aksepterer HighscoreList kun int/ Integer-objekter).
  • Object getElementvoid addElement(int, Object- henter elementet i den observerbare listen på legger til et element på posisjonen angitt av argumentet, men bare dersom det aksepteres som element. Dersom elementet ikke aksepteres, så skal IllegalArgumentException utløses. Dersom posisjonen er ulovlig så skal IndexOutOfBoundsException utløses.
  • void addElement(int, Object) - legger til et element i den observerbare listen på posisjonen angitt av argumentetbakerst i lista, men bare dersom det aksepteres som element. Dersom elementet ikke aksepteres, så skal IllegalArgumentException utløses.
  • void removeElement(int) - fjerner et element i den observerbare listen elementet på posisjonen angitt av argumentet.

Husk at metodene bør være innkapslet så stramt som mulig.

Ekstraoppgave

Implementer en HighscoreList for TicTacToe, Sokoban eller Sudoku. Etter hvert ferdige spill skal spilleren få mulighet til å legge inn resultatene i en highscore-liste, som består av navn på spilleren og antall trekk. Hvert resultat lagres som et eget objekt, og denne resultatobjektklassen må implementere Comparable-grensesnitt. Resultatobjektet legges inn i highscore-listen, som printes etter at spilleren har fått mulighet til å registrere sitt resultat.

  • Dersom posisjonen er ulovlig så skal IndexOutOfBoundsException utløses.

ObservableHighscoreList skal endres slik at den i størst mulig grad bruker metodene som arves fra ObservableList, men forøvrig ikke endrer oppførsel. Kjør hovedprogramklassen ObservableHighscoreListProgram for å sjekke at dette faktisk stemmer.


Klassediagrammet viser hvordan klassene henger sammen, og hvor metodene nå er deklarert/implementert. Merk at addElement- og removeElement-metodene er angitt som protected (ruter-symbolet), slik at kun subklasser skal kunne bruke dem.

PlantUML Macro
class ObservableList {
	Collection<Object> elements
	int size()
	Object getElement(int)
	{abstract} boolean acceptsElement(Object)
	#addElement(int, Object)
	#addElement(Object)
	#removeElement(int)
	addObservableListListener(ObservableListListener)
	removeObservableListListener(ObservableListListener)
}

class ObservableHighscoreList {
	int maxSize
	ObservableHighscoreList(int)
	boolean acceptsElement(Object)
	void addResult(int)
}

ObservableList <|-- ObservableHighscoreList

interface ObservableListListener {
	listChanged(ObservableList, int)
}
ObservableList -right-> "*" ObservableListListener: observableListListeners

class ObservableHighscoreListProgram {
	ObservableHighscoreList highscoreList
	void init()
	void run()
}
ObservableListListener <|.. ObservableHighscoreListProgram


Testkode for denne oppgaven finner du her: patterns/observable/ObservableListTest.java. Original-koden (jextest) finner du her: patterns/observable/ObservableList.jextest.

Exercise-panelet

Bruk av Exercise-panelet er obligatorisk for denne øvingen. Du må ha panelet åpent med HighscoreList.ex-filen (tests > patterns.observable > ObservableListInheritance.ex) i før du begynner med oppgaven. For mer informasjon/hjelp, se nederst på forrige side, altså hovedsiden for Øving 9.


Include Page
Bunntekst for oppgaver
Bunntekst for
Include Page
Bunntekst for JExercise-oppgaverBunntekst for JExercise- oppgaver