I denne oppgaven tar vi utgangspunkt i et StringGrid-grensesnitt som definerer metodene til et rutenett som holder strenger. Et rutenett er firkantet og består av et antall rader og kolonner. Det skal være mulig å spørre rutenettet hvilken streng som er på angitt rad og kolonne i tillegg til å endre strengen på angitt rad og kolonne. Denne oppførselen er oppsummert i det definerte StringGrid-grensesnittet under:
package interfaces; public interface StringGrid { public int getRowCount(); public int getColumnCount(); public String getElement(int row, int column); public void setElement(int row, int column, String element); }
Alle klasser som implementerer StringGrid-grensesnittet må støtte de fire definerte metodene.
Del 1 - StringGrid-grensesnitt og implementerende StringGridImpl-klasse
Lag en StringGridImpl-klasse som implementerer StringGrid-grensesnittet definert over. Du står fritt til å velge hvordan metodene definert av grensesnittet skal implementeres så lenge den tilfredsstiller den definerte oppførselen. To mulige løsninger for representasjon av rutenettet er en enkel ArrayList<String> eller en dobbel ArrayList<ArrayList<String>> der den ytre listen er radene og de indre kolonnene. Wikisiden om todimensjonale matriser kan være lurt å ta en titt på før du går i gang hvis du velger sistnevnte rutenettrepresentasjon. Dersom getElement(int, int)- eller setElement(int, int, String)-metodene kalles med ulovlige verdier for rad eller kolonne (i.e. de er utenfor rutenettet), skal en IllegalArgumentException kastes.
Del 2 - StringGridIterator-klasse
Det er hensiktmessig å kunne iterere over et rutenett som implementerer grensesnittet StringGrid - f.eks. når en bygger en streng i en toString()-metode eller skal sjekke om et spill har blitt avsluttet / vunnet. For å kunne gjøre dette skal vi nå lage en iteratorklasse for StringGrid-grensesnitt, kalt StringGridIterator. Denne klassen må implementere grensesnittet Iterator<String>, ha et felt for et objekt av typen StringGrid og støtte metodene definert av grensesnittet ved å iterere gjennom dette objektet. For å kunne velge om en vil iterere over rutenettet bortover først (rowMajor=true) eller nedover først (rowMajor=false) må du også ha et felt boolean rowMajor som holder styr på hvordan rutenettet skal itereres. StringGridIterator-klassen må altså støtte følgende konstruktør / metoder:
- StringGridIterator(StringGrid, boolean) - konstruktør som tar inn StringGrid-objektet som StringGridIterator-klassen skal iterere over i tillegg til en boolsk verdi som angir om iterasjonen skal være bortover først (rowMajor=true) eller nedover først (rowMajor=false).
boolean hasNext() - returnerer true så lenge det er flere strenger igjen i StringGrid-objektet som ikke enda er blitt iterert over (i.e. sjekk om du har kommet til siste rute i rutenettet).
String next() - returnerer den neste strengen i rutenettet. Hvilken streng som er den neste til å bli returnert avhenger av hvordan rutenettet skal itereres (i.e. om rowMajor=true eller rowMajor=false).
void remove() - denne metoden skal bare kaste et unntak av typen UnsupportedOperationException siden det ikke skal være mulig å fjerne strenger fra rutenettet.
Iterator-objektet som returneres av iterator()-metoden i StringGridImpl-klassen itererer over StringGridImpl-klassen på én bestemt måte.
Del 3 - StringGridIterable-grensesnitt og implementerende StringGridIterableImpl-klasse
Som en siste utvidelse, ønsker vi nå å gjøre StringGrid-grensesnittet itererbart. Ved å legge til extends Iterable<String> bak grensesnittnavnet StringGrid tvinger vi alle klasser som implementerer gresesnittet til også å være itererbare. Da skal det være mulig å iterere over implementerede klasse, f.eks. StringGridImpl-klassen du lagde i Del 1, ved å spørre Iterator-objektet om neste streng i rutenettet. Dette modifiserte grensesnittet (som arver fra Iterable<String>), kalt StringGridIterable, er vist i kodeblokken under:
package interfaces; public interface StringGridIterable extends Iterable<String> { public int getRowCount(); public int getColumnCount(); public String getElement(int row, int column); public void setElement(int row, int column, String element); }
public Iterator<String> iterator()
som returnerer et Iterator<String>-objekt som skal kunne spørres om neste streng i rutenettet for iterasjon. Du skal nå lage en siste klasse StringGridIterableImpl, hvor du kopierer koden fra StringGridImpl, men skal implementere grensesnittet StringGridIterable i stedet for StringGrid. Dette betyr i praksis at du bare trenger å legge til implementasjon av iterator()-metoden beskrevet over i StringGridIterableImpl-klassen. Her kan det være lurt å ta i bruk StringGridIterator-klassen du laget i Del 2 av denne oppgaven.
JExercise lar deg sjekke din egen kode vha. forhåndslagde JUnit-tester og JExercise-panelet.
Bruk av JExercise:
- Sørg for at jexercise-standalone.jar er lagt til i ditt prosjekts Build Path. Dette må gjøres hver gang du oppretter et nytt prosjekt, og det er derfor lurt å gjenbruke samme prosjekt til alle oppgaver.
- JExercise-tillegget må være installert. Installer tillegget fra følgende oppdateringsadresse: http://folk.ntnu.no/hal/dev/updatesite.
- Åpne JExercise-panelet via Window -> Show View -> Other, og navigere deg fram til JExercise i vinduet som kommer opp, velge det og klikke OK.
- Klikke og dra oppgavens testklasse, <oppgavenavn>Test.java fra pakkeoversikten og slippe den i JExercise panelet.
- Testene kan så kjøres ved å dobbeltklikke på testen som ønskes kjørt.
Ved trøbbel, se først om du finner løsningen i Løsninger på trøbbel med JExercise.
Twitre gjerne om oppgaven når du er ferdig: <a href="https://twitter.com/share" class="twitter-share-button" data-hashtags="jexercise">Tweet</a>
<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs');</script>