diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..04cd618 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "java.configuration.updateBuildConfiguration": "automatic" +} diff --git a/oppgavetekster/README.md b/oppgavetekster/README.md index 07d7b24..4092521 100644 --- a/oppgavetekster/README.md +++ b/oppgavetekster/README.md @@ -2,12 +2,12 @@ Denne mappen inneholder øvingstekster for TDT4100 - Objektorientert programmering våren 2021. Tabellen under inneholder linker til hver enkelt oppgavetekst og tema for øvingen. Linker vil bli oppdatert underveis. -| Øving | Tema | -| ----------------- | ---------------------------------------- | +| Øving | Tema | +| --------------------------- | ---------------------------------------- | | [Øving 1](oving1/README.md) | Tilstand og oppførsel | -| Øving 2 | Innkapsling og validering | -| Øving 3 | Klasser og testing | -| Øving 4 | Objektstrukturer | -| Øving 5 | Grensesnitt | -| Øving 6 | Observatør-observert og delegering | -| Øving 7 | Arv og abstrakte klasser | \ No newline at end of file +| [Øving 2](oving2/README.md) | Innkapsling og validering | +| [Øving 3](oving3/README.md) | Klasser og testing | +| [Øving 4](oving4/README.md) | Objektstrukturer | +| [Øving 5](oving5/README.md) | Grensesnitt | +| [Øving 6](oving6/README.md) | Observatør-observert og delegering | +| [Øving 7](oving7/README.md) | Arv og abstrakte klasser | diff --git a/oppgavetekster/classpath_fix/README.md b/oppgavetekster/classpath_fix/README.md new file mode 100644 index 0000000..ac95f81 --- /dev/null +++ b/oppgavetekster/classpath_fix/README.md @@ -0,0 +1,18 @@ +# Fiks ved problemer med å pulle i Eclipse + +Dersom du får problemer med å pulle øvinger i Eclipse, med feilmelding som vist i bildet under ("Checkout conflict with files: `/.classpath`), kan du følge denne guiden for å løse problemet. + +**NB!** Bildene i denne guiden går ut i fra at problemfilen ligger i `ovinger`-prosjektet, men det kan også være under et av de andre prosjektene. +Du kan se hvilke filer som er problematiske i feilmeldinga du får når du puller. + +![Feilmelding ved pull](img/2.png) + +| Beskrivelse | Bilde | +|------|-------| +| I Project-explorer i Eclipse, trykk på de tre prikkene i øvre høyre hjørne. I menyen som kommer opp velger du *Filters and Customization...* (I MacOS står det kun *Filters...*). | ![Meny](img/3.png) ![Filters and customization](img/4.png) | +| I vinduet som kommer opp, pass på at valget `.* resources` **IKKE** er valgt, og trykk ok. | ![Vis skjulte](img/5.png) | +| Åpne prosjektet du hadde konflikter i, dette er sannsynligvis ovinger. Du vil se at det har dukket opp en fil som heter `.classpath` under prosjektmappa. | ![Prosjekt med .classpath](img/6.png) | +| Høyreklikk på `.classpath`-fila, og velg *Replace With* -> *HEAD Revision*. Trykk *Discard Changes* i vinduet som kommer opp. | ![Replace with](img/7.png)) | +| Nå kan du prøve å pulle igjen (høyreklikk på prosjektet og velg *Team* -> *Pull*). Sannsynligvis vil prosjektet nå se ganske kaotisk ut, med veldig mange mapper. Det fikser du enkelt ved å høyreklikke på det, og velge *Maven* -> *Update Project*, velge alle prosjektene, og trykke *OK* | ![Mvn update](img/9.png) | + +Etter denne prosessen skal alt fungere som før. Har du problemer, ta kontakt med undass. \ No newline at end of file diff --git a/oppgavetekster/classpath_fix/img/1.png b/oppgavetekster/classpath_fix/img/1.png new file mode 100644 index 0000000..8b18467 Binary files /dev/null and b/oppgavetekster/classpath_fix/img/1.png differ diff --git a/oppgavetekster/classpath_fix/img/2.png b/oppgavetekster/classpath_fix/img/2.png new file mode 100644 index 0000000..73914d6 Binary files /dev/null and b/oppgavetekster/classpath_fix/img/2.png differ diff --git a/oppgavetekster/classpath_fix/img/3.png b/oppgavetekster/classpath_fix/img/3.png new file mode 100644 index 0000000..42472e1 Binary files /dev/null and b/oppgavetekster/classpath_fix/img/3.png differ diff --git a/oppgavetekster/classpath_fix/img/4.png b/oppgavetekster/classpath_fix/img/4.png new file mode 100644 index 0000000..2cfb2d1 Binary files /dev/null and b/oppgavetekster/classpath_fix/img/4.png differ diff --git a/oppgavetekster/classpath_fix/img/5.png b/oppgavetekster/classpath_fix/img/5.png new file mode 100644 index 0000000..7aa27d2 Binary files /dev/null and b/oppgavetekster/classpath_fix/img/5.png differ diff --git a/oppgavetekster/classpath_fix/img/6.png b/oppgavetekster/classpath_fix/img/6.png new file mode 100644 index 0000000..7b8a514 Binary files /dev/null and b/oppgavetekster/classpath_fix/img/6.png differ diff --git a/oppgavetekster/classpath_fix/img/7.png b/oppgavetekster/classpath_fix/img/7.png new file mode 100644 index 0000000..09299b4 Binary files /dev/null and b/oppgavetekster/classpath_fix/img/7.png differ diff --git a/oppgavetekster/classpath_fix/img/8.png b/oppgavetekster/classpath_fix/img/8.png new file mode 100644 index 0000000..d1fcba3 Binary files /dev/null and b/oppgavetekster/classpath_fix/img/8.png differ diff --git a/oppgavetekster/classpath_fix/img/9.png b/oppgavetekster/classpath_fix/img/9.png new file mode 100644 index 0000000..b6dfb2a Binary files /dev/null and b/oppgavetekster/classpath_fix/img/9.png differ diff --git a/oppgavetekster/oving5/CardContainer.md b/oppgavetekster/oving5/CardContainer.md index b3926c7..5cbd680 100644 --- a/oppgavetekster/oving5/CardContainer.md +++ b/oppgavetekster/oving5/CardContainer.md @@ -1,7 +1,7 @@ # Interface - CardContainer-oppgave Denne oppgaven handler om å lage et felles grensesnitt for `CardDeck`- og `CardHand`-klassene, laget i oppgavene [Innkapsling - Card-oppgave](../oving3/Card.md) og -[Objektstrukturer - Card-oppgave del 2](../oving5/Card.md). +[Objektstrukturer - Card-oppgave del 2](../oving4/Card.md). Her skal du lage og implementere et grensenitt kalt `CardContainer`, som spesifiserer metoder for lesing av samlinger av Card-objekter. #### Del 1 - CardContainer interface diff --git a/oppgavetekster/oving7/AbstractAccount.md b/oppgavetekster/oving7/AbstractAccount.md new file mode 100644 index 0000000..e02e41d --- /dev/null +++ b/oppgavetekster/oving7/AbstractAccount.md @@ -0,0 +1,74 @@ +# Arv - AbstractAccount-oppgave +Denne oppgaven handler om å lage en felles abstrakt superklasse `AbstractAccount` +for `CreditAccount`, `DebitAccount`- og `SavingsAccount2`-klassene. + +Denne oppgaven er en annen variant av +[SavingsAccount](./SavingsAccount.md)-oppgaven, med fokus +på bruk av abstrakte klasser og arv. + +### Del 1 - Abstrakt klasse AbstractAccount +En bank består av mange ulike type kontoer: sparekontoer, brukskontoer, +depositumskontoer, støttekontoer etc. Siden disse har mye felles, f.eks. +har alle en balanse, så er det praktisk å samle så mye som mulig av den +felles logikken i en superklasse, som alle kan arve fra. Denne superklassen +er imidlertid ikke noen egen type konto, og derfor gjør vi den abstrakt, +slik at den ikke kan instansieres. De konkrete konto-klassene som arver +fra den, må selvsagt være instansierbare. + +Metodene i `AbstractAccount`-klassen, er omtrent de samme som dem vi definerte +i `Account`-grensesnittet i [SavingsAccount](./SavingsAccount.md)-oppgaven, +og er som følger: + +* `void deposit(double)` - øker kontobalansen med innskutt beløp. Merk at det +innskutte beløpet må være positivt. Ved ulovlig innskudd skal en +`IllegalArgumentException` utløses. +* `void withdraw(double)` - Metoden kaller internalWithdraw(uttaksbeløp), +som implementeres i hver subklasse. Hvis uttaksbeløpet er negativt skal +metoden utløse en `IllegalArgumentException`. +* `abstract void internalWithdraw(double)` - minsker kontobalansen med beløpet +som blir tatt ut. Merk at reglene for uttak er ulik for klassene som +implementerer `AbstractAccount`, og må derfor implementeres i hver klasse. +Hvis det ikke er mulig å ta ut det angitte beløpet skal metoden utløse en +`IllegalStateException`. +* `double getBalance()` - returnerer kontobalansen. + +Alle metodene utenom den absktrakte må implementeres. I tillegg må +`AbstractAccount` ha en tilstand *balance* for saldo på kontoen. +Saldoen skal settes til 0 i konstruktøren. + +Vær oppmerksom på at du i Del 2 skal lage subklasser av `AbstractAccount` +og at du ved å bruke rett innkapsling (hint: `protected`-modifikatoren) skal +la *subklassene* nyttiggjøre seg *superklassen* i størst mulig grad. + + +### Del 2 - DebitAccount extends AbstractAccount +En debetkonto er den enkleste formen for konto, hvor det eneste kravet er at +saldoen til enhver tid må være større eller lik 0. `DebitAccount` skal utvide +(arve fra med `extends`) `AbstractAccount` og sikre at saldoen aldri blir +lavere enn 0. + +Testkode for oppgavene finner du her: [inheritance/DebitAccountTest.java](../../src/test/java/inheritance/DebitAccountTest.java). + +### Del 3 - CreditAccount extends AbstractAccount +En `CreditAccount` har i tillegg til *balance* en tilstand for *creditLine*, +altså tilgjengelig kreditt på kontoen. Denne kredittlinjen tillater at kontoen +kan overtrekkes (at saldoen er negativ) innenfor kredittlinjen. Klassen må ha +*tilgangsmetoder* (getters and setters) for *creditLine*. Merk at +kredittlinjen alltid må være større eller lik 0, hvis ikke skal det utløses +en `IllegalArgumentException`. Hvis en ny kredittlinje settes og balansen er +negativ, må den nye kredittlinjen dekke den eksisterende balansen. Ellers skal +det utløses en `IllegalStateException` og ingen endring i kredittlinjen. + +Konstruktøren `CreditAccount(double)` skal sette kredittlinjen. + +Testkode for oppgavene finner du her: [inheritance/CreditAccountTest.java](../../src/test/java/inheritance/CreditAccountTest.java). + +### Del 4 - SavingsAccount2 extends AbstractAccount +En `SavingsAccount2` (*merk at navnet er endret, for ikke å kræsje med +SavingsAccount-klassen fra den tidligere oppgaven!*) kan kun ha positiv saldo. +I tillegg har kontoen uttaksbegrensinger. En `SavingsAccount2` har *x* antall +*uttak* (**withdrawals**). Dersom man ønsker å ta ut penger etter alle uttak er +brukt opp, skal saldoen belastes med et *gebyr* (**fee**). Både **withdrawals** +(antall) og **fee** (beløp) settes i konstruktøren `SavingsAccount2(int, double)`. + +Testkode for oppgavene finner du her: [inheritance/SavingsAccount2Test.java](../../src/test/java/inheritance/SavingsAccount2Test.java). \ No newline at end of file diff --git a/oppgavetekster/oving7/CardContainerImpl.md b/oppgavetekster/oving7/CardContainerImpl.md new file mode 100644 index 0000000..fca2e5c --- /dev/null +++ b/oppgavetekster/oving7/CardContainerImpl.md @@ -0,0 +1,38 @@ +# Arv - CardContainerImpl-oppgave + +Denne oppgaven handler om å lage en felles superklasse `CardContainerImpl` +for `CardDeck`- og `CardHand`-klassene, laget i [Card-oppgaven](../oving4/Card.md) +og [CardContainer-oppgaven](../oving5/CardContainer.md). + +**Merk:** Om du ikke har gjort Card-oppgaven og CardContainer-oppgaven allerede, +kan du bruke løsningsforslaget som er lagt ut for disse under `lf/src/interfaces`. + +### Del 1 - Superklassen CardContainerImpl +Lag en `CardContainerImpl`-superklasse, som implementerer grensesnittet +`CardContainer` (se [CardContainer-oppgaven](../oving5/CardContainer.md)) og +inneholder koden som er felles for `CardDeck` og `CardHand`. + +La `CardDeck` og `CardHand` arve `CardContainerImpl` og gjør nødvendige endringer +i disse klassene, slik at totaloppførselen er som før. F.eks. skal `CardDeck` +-objektet ha samme konstruktør som før, som skal sikre samme initielle tilstand +(men ikke nødvendigvis med samme konstruktør-kode). + +Merk at målet er at mest mulig kode skal flyttes til *superklassen* og gjenbrukes +i *subklassene*. Det er viktig å bruke innkapsling rett +(hint: `protected`-modifikatoren) for å nyttiggjøre seg superklassen i størst +mulig grad. + +### Del 2 - Regler for maksimalt antall kort +Anta at en ønsker å unngå at instanser av `CardContainerImpl` (eller av en av +subklassene) inneholder for mange kort. Legg til et *privat* `maxCardCount`-felt +i `CardContainerImpl`, en konstruktør som *initialiserer* feltet og en *getter* +for å lese verdien. Legg så til evt. endre kode i `CardContainerImpl` som sikrer +at antall kort ikke overstiger dette tallet og at subklassene ikke kan omgå +denne valideringen. + +`CardContainerImpl`-subklassene `CardDeck` og `CardHand` skal sette maks-antallet +som følger: `CardDeck` skal sette makstallet til *52* og `CardHand` skal ta +inn maks-antallet i *sin* konstruktør. Hvis man forsøker å legge til flere kort +enn hva som er tillatt i `CardHand`, skal det utløses en `IllegalStateException`. + +Testkode for oppgavene finner du her: [inheritance/CardDeckTest.java](../../src/test/java/inheritance/CardDeckTest.java) og [inheritance/CardHandTest.java](../../src/test/java/inheritance/CardHandTest.java). \ No newline at end of file diff --git a/oppgavetekster/oving7/ObservableList.md b/oppgavetekster/oving7/ObservableList.md new file mode 100644 index 0000000..357f700 --- /dev/null +++ b/oppgavetekster/oving7/ObservableList.md @@ -0,0 +1,140 @@ +# Observatør-observert-teknikken og arv - HighscoreList-oppgave med ObservableList +Denne oppgaven handler om å bruke observatør-observert-teknikken for å bli +informert om endringer i en highscore-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. + +Klassene skal legges i `src/main/java/patterns.observable/` og tilhørende +tester ligger i `src/test/java/patterns.observable/`. + +### Del 1: Implementasjon av 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 hvilken +posisjon som ble endret. +* `addObservableListListener(ObservableListListener)` - registrerer en ny lytter +* `removeObservableListListener(ObservableListListener)` - fjerner en tidligere +registrert lytter + +Klassediagram for `HighscoreList`, `ListListener` og `ObservableList`: + + +Testkode for denne oppgaven finner du her: [patterns/observable/ObservableHighscoreListTest.java](../../src/test/java/patterns/observable/ObservableHighscoreListTest.java). + +### Del 2: Hovedprogram 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ørsel: + +* `void init()` - oppretter en ny `ObservableHighscoreList` og registrerer seg +selv (altså `ObservableHighscoreListProgram`-instansen) som lytter +* `void run()` - leser inn tall (resultater) fra konsollet og legger dem til i +listen +* `void listChanged(ObservableList, int)` - observerer endringer i +`ObservableHighscoreList`-instansen og skriver ut posisjonsargumentet, samt +selve listen, til konsollet + +Klassediagrammet viser hvordan klassene henger sammen, og vårt forslag til +metoder: + + +### Del 3: ObservableList +Den abstrakte superklassen `ObservableList` skal legges til som en generell +superklasse for observerbare lister, som `ObservableHighscoreList` skal arve +fra. Denne klassen skal både holde 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 +* `abstract boolean acceptsElement(Object)` - returnerer hvorvidt *subklassen* +aksepterer at objektet legges inn i lista (f.eks. aksepterer `HighscoreList` +kun `Integer`-objekter). +* `void addElement(int, Object)` - 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(Object)` - legger til et element bakerst i lista, men bare +dersom det *aksepteres* som element. Dersom elementet ikke aksepteres, så skal +`IllegalArgumentException` utløses. +* `void removeElement(int)` - fjerner elementet på posisjonen angitt av +argumentet. 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. + + +Testkode for denne oppgaven finner du her: [patterns/observable/ObservableListTest.java](../../src/test/java/patterns/observable/ObservableListTest.java). + diff --git a/oppgavetekster/oving7/README.md b/oppgavetekster/oving7/README.md new file mode 100644 index 0000000..45e7e6a --- /dev/null +++ b/oppgavetekster/oving7/README.md @@ -0,0 +1,40 @@ +# Øving 07: Arv og abstrakte klasser + +**Øvingsmål** +* Lære hvordan arv-mekansimen brukes i OO +* Lære om instanser, typer, deklarasjoner og tilordninger +* Lære om sub- og superklasser samt om synlighetsmodifikatorer som brukes ved arv +* Lære om abstrakte klasser, deres bruksområder og fordeler + +**Øvingskrav** +* Kunne bruke arv til å modellerere enkle(re) objektstrukturer- og relasjoner i Java +* Kunne la flere subklasser bruke funksjonalitet definert i samme superklasse +* Kunne la en subklasse redefinere metoder definert i en superklasse +* Kunne samle felles oppførsel til to eller flere subklasser i en felles abstrakt klasse + +## Dette må du gjøre +Oppgavene skal lagres i `ovinger/src/main/java/inheritance`. + +I begge delene er antageligvis vanskelighetsgraden stigende. Alle er høyst eksamensrelevante og det anbefales følgelig å ta en titt på samtlige. + +### Del 1: Arv +Velg og gjennomfør *minst én* av oppgavene om arv: + +* [CardContainerImpl](./CardContainerImpl.md) +* [Train](./Train.md) +* [SavingsAccount](./SavingsAccount.md) + +### Del 2: Abstrakte klasser og arv +Velg og gjennomfør *minst én* av oppgavene om abstrakte klasser og arv: + +* [AbstractAccount](./AbstractAccount.md) +* [ObservableList](./ObservableList.md) + + +### Hjelp / mistanke om bugs + +Ved spørsmål eller behov for hjelp konsulter studassen din i saltiden hans / hennes. Du kan også oppsøke andre studasser på sal eller legge ut et innlegg på [Piazza](https://piazza.com/). + +### Godkjenning + +Last opp kildekode på Blackboard innen den angitte innleveringsfristen. Innlevert kode skal demonstreres for en læringsassistent innen én uke etter innleveringsfrist. Se for øvrig Blackboard-sidene for informasjon rundt organisering av øvingsopplegget og det tilhørende øvingsreglementet. diff --git a/oppgavetekster/oving7/SavingsAccount.md b/oppgavetekster/oving7/SavingsAccount.md new file mode 100644 index 0000000..b933b6b --- /dev/null +++ b/oppgavetekster/oving7/SavingsAccount.md @@ -0,0 +1,117 @@ +# Arv - SavingsAccount-oppgave +Denne oppgaven handler om å lage en felles superklasse `SavingsAccount` for +`BSU`- og `ForeldreSpar`-klassene. `SavingsAccount` skal dessuten implementere +`Account`-grensesnittet. + +Denne oppgaven bygger videre på `Account`-oppgavene under +[Innkapsling](../oving2/Account.md) og [Tilstand og oppførsel](../oving1/Account.md). + +### Del 1 - SavingsAccount implements Account +En bank består av mange ulike type kontoer: sparekontoer, brukskontoer, +depositumskontoer, støttekontoer etc. Felles for alle kontoer er +`Account`-grensesnittet, som er definert under: + +```java +package inheritance; + +public interface Account { + public void deposit(double amount); + public void withdraw(double amount); + public double getBalance(); +} +``` + +Vi skal i denne oppgaven fokusere på sparekontoer og du skal nå lage en +`SavingsAccount`-superklasse, som implementerer `Account`-grensesnittet. +Funksjonaliteten som hver av metodene definert i grensesnittet over skal +støtte er: + +* `void deposit(double)` - øker kontobalansen med innskutt beløp. Merk at det +innskutte beløpet må være positivt. Ved ulovlig innskudd skal en +`IllegalArgumentException` utløses. +* `void withdraw(double)` - minsker kontobalansen med beløpet som blir tatt ut. +Merk at uttaksbeløpet må være positivt, ellers skal et unntak av typen +`IllegalArgumentException` utløses. Dersom det ikke er dekning på kontoen +(en `SavingsAccount` kan ikke ha negativ balanse) skal et unntak av typen +`IllegalStateException` utløses. +* `double getbalance()` - returnerer kontobalansen. + +I tillegg til å støtte `Account`-grensesnittet over, som er felles for alle +kontoer, skal sparekontoer ha en rentefot og en metode som forrenter kontoen. +Denne kalles av bankene for hver sparekonto på slutten av året slik at alle +dets kunder opptjener renter (ikke tenk på at banker egentlig holder styr på +hvor stor balansen har vært gjennom hele året eller forrenter kontoen +kontinuerlig - her skal vi bare anta at innestående kontobalanse ved årsslutt +forrentes i sin helhet) - derfor heter metoden `endYearUpdate()`. I tillegg +skal `SavingsAccount`-klassen ha en konstruktør som tvinger alle objekter +av denne typen til å bli instansiert med en rentefot. Dette er oppsummert her: + +* `SavingsAccount(double)` - konstruktør som tar inn rentefoten på kontoen +(et desimaltall, f.eks. 0.05 tilsvarer en rente på 5 %). Åpningsbalansen +skal være 0. +* `void endYearUpdate()` - forrenter kontobalansen basert på rentefoten. +Vi tenker oss at denne kalles av kode utenfor denne klassen, f.eks. resten +av et tenkt banksystem ved årsoppgjør, som et signal på at nå er et nytt år +over. + +Vær oppmerksom på at du i Del 2 og 3 skal lage *subklasser* av `SavingsAccount` +og at du ved å bruke rett innkapsling (hint: `protected`-modifikatoren) kan la +*subklassene* nyttiggjøre seg *superklassen* i størst mulig grad. + +Testkode for oppgavene finner du her: [inheritance/SavingsAccountTest.java](../../src/test/java/inheritance/SavingsAccountTest.java). + +### Del 2 - BSU extends SavingsAccount +I tillegg til generelle sparekontoer finnes det en spesiell type sparekonto +som heter BSU. Du skal nå lage en `BSU`-klasse som arver fra +`SavingsAccount`-superklassen. Her er målet at du skal gjenbruke mest mulig +av *superklassen* og samtidig støtte BSU-spesifikk oppførsel. En BSU-konto er, +i tillegg til å være en sparekonto, spesiell i den forstand at det kun er +lovlig å sette inn inntil et forhåndsbestemt beløp per år +(den gamle regjeringen fastslo at BSU-kontoer i 2014 skulle ha en +innskuddsgrense på kr 25 000, men din kode skal ha støtte for å ha en vilkårlig +grense) og at det kun er lovlig å ta ut av det beløpet som er satt inn siste +år. M.a.o. vil en ved årsskifte få mulighet til å sette inn nye innskudd +innenfor innskuddsgrensen, men en har ikke lenger mulighet til å ta ut hele +balansen (innskudd fra tidligere år låses). Dessuten gir en vanlig BSU-konto +20% skattefradrag for årets innskudd. + +Du må selv avgjøre hvilke felt som må legges til for å støtte den beskrevne +oppførsel. I tillegg stilles følgende krav til klassen: + +* `BSU(double, double)` - konstruktør som tar inn rentefoten på kontoen og et +desimaltall som angir hvor mye det er tillatt å sette inn på kontoen per år. +* `double getTaxDeduction()` - returnerer skattefradrag for inneværende år. +Dette vil være 20% av innskutt(e) beløp siste år. + +Testkode for oppgavene finner du her: [inheritance/BSUTest.java](../../src/test/java/inheritance/BSUTest.java). + +### Del 3 - ForeldreSpar extends SavingsAccount +En annen spesiell type sparekonto, her kalt ForeldreSpar, har et begrenset +antall lovlige uttak per år (ofte i bytte mot en høyere rente). Du skal nå +lage en slik `ForeldreSpar`-klasse som arver fra `SavingsAccount`-superklassen. +Her er igjen målet at du skal gjenbruke mest mulig av *superklassen* og +samtidig støtte ForeldreSpar-spesifikk oppførsel. Denne klassen skal sikre at +kun det lovlige antallet uttak gjøres i løpet av et år. + +Du må selv avgjøre hvilke felt som må legges til før å støtte den beskrevne +oppførsel. I tillegg stilles følgende krav til klassen: + +* `ForeldreSpar(double, int)` - konstruktør som tar inn rentefoten på kontoen +og et heltall som angir antall lovlige uttak per år. +* `int getRemainingWithdrawals()` - returnerer antall gjenstående uttak fra +sparekontoen. + +Testkode for oppgavene finner du her: [inheritance/ForeldreSparTest.java](../../src/test/java/inheritance/ForeldreSparTest.java). + + + + + + + + + + + + + diff --git a/oppgavetekster/oving7/Train.md b/oppgavetekster/oving7/Train.md new file mode 100644 index 0000000..97374c9 --- /dev/null +++ b/oppgavetekster/oving7/Train.md @@ -0,0 +1,72 @@ +# Arv - Train-oppgave + +I denne oppgaven skal vi modellere to typer togvogner og bruke dem i et tog. Vi vil bruke +arv og samle det som er felles for togvognene i en *superklasse*. + +### Del 1 - TrainCar + + +I denne delen skal du lage en klasse kalt `TrainCar` for en enkel og generell +togvogn, med følgende funksjonalitet (se også diagrammet over): + +* `TrainCar(int)` - en konstruktør som tar inn hvor mye en tom vogn veier. +* `int getTotalWeight` - returnerer vognas totale vekt. Merk at denne også skal +kunne kalles på *subklasser* og fortsatt returnere totalvekta til vogna +(stikkord: *redefinering*). +* `setDeadWeight(int)` - setter hvor mye en tom vogn veier. Altså vekten til +kun vognen, uten passasjerer eller last. +* `int getDeadWeight()` - returnerer hvor mye en tom vogn veier. Altså vekten til +kun vognen, uten passasjerer eller last. + +Testkode for oppgaven finner du her: [inheritance/TrainCarTest.java](../../src/test/java/inheritance/TrainCarTest.java). + +### Del 2 - CargoCar og PassengerCar + + +I denne delen skal du lage to forskjellige typer togvogner som er spesialiserte +for sitt bruk. Begge skal arve fra `TrainCar`. +##### CargoCar extends TrainCar: +Denne klassen skal gjenspeile en lastevogn som frakter diverse ting og tang. +Følgende funksjonalitet trengs (se også diagrammet over): + +* `CargoCar(int, int)` - her tas inn hvor mye en tom vogn veier (som i `TrainCar`), +og hvor mye vogna sin last veier. +* `int getCargoWeight()` - returnerer hvor mye lasten veier. +* `setCargoWeight(int)` - setter en ny verdi for vekten til lasten. + +##### PassengerCar extends TrainCar: +Denne klassen gjenspeiler en passasjervogn. Legg til følgende metoder +(se også diagrammet over): + +* `PassengerCar(int, int)` - her tas inn hvor mye en tom vogn veier +(som i `TrainCar`), og hvor mange passasjerer det er i vogna. +* `int getPassengerCount()` - returner antall passasjerer. +* `setPassengerCount(int)` - setter en ny verdi for antall passasjerer. + +For å beregne totalvekta, så kan du anta at en gjennomsnittspassasjer veier 80 kg. + +Testkode for oppgavene finner du her: [inheritance/PassengerCarTest.java](../../src/test/java/inheritance/PassengerCarTest.java) og [inheritance/CargoCarTest.java](../../src/test/java/inheritance/CargoCarTest.java). + + +### Del 3 - Train + + +Klassen `Train` skal forestille et tog bestående av et sett vogner. +Klassen skal ha følgende metoder (se også diagrammet over): + +* `addTrainCar(TrainCar)` - denne metoden skal ta inn en togvogn og knytte den +til dette lokomotivet. +* `boolean contains(TrainCar)` - Sjekker om lokomotivet har `TrainCar`-argument +knyttet til seg. +* `int getTotalWeight()` - returner alle vognene sin totale vekt. Vi tar ikke +høyde for lokomotivet sin eventuelle vekt. +* `int getPassengerCount()` - tilsvarende `PassengerCar` sin metode, men +returnerer antallet for alle vognene. +* `int getCargoWeight()` - tilsvarende `CargoCar` sin metode, men returnerer +lastevekten for alle vognene. +* `String toString()` - `toString`-metoden skal sette sammen en *String* med +oversikt over alle vognene som er knyttet til den. For hver vogn skal vogntype +og totalvekt være med. Passasjervogner skal i tillegg ha med antall passasjerer +og lastevogner skal ha med hvor mye lasten veier. + +Testkode for oppgaven finner du her: [inheritance/TrainTest.java](../../src/test/java/inheritance/TrainTest.java). \ No newline at end of file diff --git a/oppgavetekster/oving7/images/ObservableList_del1.png b/oppgavetekster/oving7/images/ObservableList_del1.png new file mode 100644 index 0000000..a5f13b9 Binary files /dev/null and b/oppgavetekster/oving7/images/ObservableList_del1.png differ diff --git a/oppgavetekster/oving7/images/ObservableList_del2.png b/oppgavetekster/oving7/images/ObservableList_del2.png new file mode 100644 index 0000000..77c5707 Binary files /dev/null and b/oppgavetekster/oving7/images/ObservableList_del2.png differ diff --git a/oppgavetekster/oving7/images/ObservableList_del3.png b/oppgavetekster/oving7/images/ObservableList_del3.png new file mode 100644 index 0000000..e6800f4 Binary files /dev/null and b/oppgavetekster/oving7/images/ObservableList_del3.png differ diff --git a/oppgavetekster/oving7/images/Train_del1.png b/oppgavetekster/oving7/images/Train_del1.png new file mode 100644 index 0000000..597e019 Binary files /dev/null and b/oppgavetekster/oving7/images/Train_del1.png differ diff --git a/oppgavetekster/oving7/images/Train_del2.png b/oppgavetekster/oving7/images/Train_del2.png new file mode 100644 index 0000000..28c2709 Binary files /dev/null and b/oppgavetekster/oving7/images/Train_del2.png differ diff --git a/oppgavetekster/oving7/images/Train_del3.png b/oppgavetekster/oving7/images/Train_del3.png new file mode 100644 index 0000000..4804604 Binary files /dev/null and b/oppgavetekster/oving7/images/Train_del3.png differ diff --git a/src/main/java/inheritance/AbstractAccount.java b/src/main/java/inheritance/AbstractAccount.java new file mode 100644 index 0000000..b7f8b9d --- /dev/null +++ b/src/main/java/inheritance/AbstractAccount.java @@ -0,0 +1,25 @@ +package inheritance; + +public abstract class AbstractAccount { + + protected double balance; + + public void deposit(double amount) { + if (amount < 0) + throw new IllegalArgumentException("Can not deposit a negative amount of money."); + this.balance += amount; + } + + public void withdraw(double amount) { + if (amount < 0) + throw new IllegalArgumentException("Can not withdraw a negative amount of money."); + internalWithdraw(amount); + } + + public abstract void internalWithdraw(double amount); + + public double getBalance() { + return balance; + } + +} diff --git a/src/main/java/inheritance/CargoCar.java b/src/main/java/inheritance/CargoCar.java new file mode 100644 index 0000000..96469ae --- /dev/null +++ b/src/main/java/inheritance/CargoCar.java @@ -0,0 +1,36 @@ +package inheritance; + +public class CargoCar extends TrainCar { + + private int cargoWeight; + + public CargoCar(int deadWeight, int cargoWeight){ + super(deadWeight); + this.setCargoWeight(cargoWeight); + } + + public int getCargoWeight() { + return this.cargoWeight; + } + + public void setCargoWeight(int cargoWeight) { + if (cargoWeight < 0) + throw new IllegalArgumentException("Cargo weight cannot be less than 0"); + this.cargoWeight = cargoWeight; + } + + @Override + public int getTotalWeight() { + return this.getDeadWeight() + this.getCargoWeight(); + } + + @Override + public String toString() { + return String.format( + "(Cargo w:%d, cw:%d)", + this.getTotalWeight(), + this.getCargoWeight() + ); + } +} + diff --git a/src/main/java/inheritance/CreditAccount.java b/src/main/java/inheritance/CreditAccount.java new file mode 100644 index 0000000..551e2be --- /dev/null +++ b/src/main/java/inheritance/CreditAccount.java @@ -0,0 +1,34 @@ +package inheritance; + +public class CreditAccount extends AbstractAccount { + + private double creditLine; + + public CreditAccount(double creditLine) { + this.setCreditLine(creditLine); + } + + public double getCreditLine() { + return creditLine; + } + + public void setCreditLine(double creditLine) { + if (creditLine < 0) + throw new IllegalArgumentException(); + + if (creditLine + super.getBalance() < 0) + throw new IllegalStateException(); + + this.creditLine = creditLine; + } + + @Override + public void internalWithdraw(double amount) { + + if (creditLine + super.getBalance() - amount < 0) + throw new IllegalStateException(); + + super.balance -= amount; + } + +} diff --git a/src/main/java/inheritance/DebitAccount.java b/src/main/java/inheritance/DebitAccount.java new file mode 100644 index 0000000..ab0b551 --- /dev/null +++ b/src/main/java/inheritance/DebitAccount.java @@ -0,0 +1,12 @@ +package inheritance; + +public class DebitAccount extends AbstractAccount { + + @Override + public void internalWithdraw(double amount) { + if (this.balance - amount < 0) + throw new IllegalStateException("The account does not contain enough money for the withdrawal."); + super.balance -= amount; + } + +} diff --git a/src/main/java/inheritance/PassengerCar.java b/src/main/java/inheritance/PassengerCar.java new file mode 100644 index 0000000..4137efe --- /dev/null +++ b/src/main/java/inheritance/PassengerCar.java @@ -0,0 +1,40 @@ +package inheritance; + +public class PassengerCar extends TrainCar { + + private int passengerCount; + private static int passengerWeight = 80; + + public PassengerCar(int deadWeight, int passengerCount) { + super(deadWeight); + this.setPassengerCount(passengerCount); + } + + public int getPassengerCount() { + return this.passengerCount; + } + + public void setPassengerCount(int passengerCount) { + if (passengerCount < 0) + throw new IllegalArgumentException("Passenger count cannot be less than 0"); + this.passengerCount = passengerCount; + } + + private int getTotalPassengerWeight() { + return this.getPassengerCount() * passengerWeight; + } + + @Override + public int getTotalWeight() { + return this.getDeadWeight() + this.getTotalPassengerWeight(); + } + + @Override + public String toString() { + return String.format( + "(Passengers w:%d, pc:%d)", + this.getTotalWeight(), + this.getPassengerCount() + ); + } +} diff --git a/src/main/java/inheritance/SavingsAccount2.java b/src/main/java/inheritance/SavingsAccount2.java new file mode 100644 index 0000000..81fe2ee --- /dev/null +++ b/src/main/java/inheritance/SavingsAccount2.java @@ -0,0 +1,38 @@ +package inheritance; + +public class SavingsAccount2 extends AbstractAccount { + + int withdrawals; + int withdrawalCount; + double fee; + + public SavingsAccount2(int withdrawals, double fee) { + this.setWithdrawals(withdrawals); + this.setFee(fee); + } + + public void setWithdrawals(int withdrawals) { + if (withdrawals < 0) + throw new IllegalArgumentException(); + this.withdrawals = withdrawals; + } + + public void setFee(double fee) { + if (fee < 0) + throw new IllegalArgumentException(); + this.fee = fee; + } + + @Override + public void internalWithdraw(double amount) { + if (withdrawalCount >= withdrawals) + amount += fee; + + if (this.balance - amount < 0) + throw new IllegalStateException("The account does not contain enough money for the withdrawal."); + + withdrawalCount++; + super.balance -= amount; + } + +} diff --git a/src/main/java/inheritance/Train.java b/src/main/java/inheritance/Train.java new file mode 100644 index 0000000..d6eb4b2 --- /dev/null +++ b/src/main/java/inheritance/Train.java @@ -0,0 +1,55 @@ +package inheritance; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.stream.Collectors; + +public class Train { + private Collection cars = new ArrayList<>(); + + public void addTrainCar(TrainCar car) { + this.cars.add(car); + } + + public boolean contains(TrainCar car) { + return this.cars.contains(car); + } + + public int getTotalWeight() { + return + this.cars + .stream() + .map(car -> car.getTotalWeight()) + .reduce(0, (a, b) -> a+b); + } + + public int getPassengerCount() { + return + this.cars + .stream() + .filter(car -> car instanceof PassengerCar) + .map(car -> (PassengerCar) car) + .map(pcar -> pcar.getPassengerCount()) + .reduce(0, (a, b) -> a+b); + } + + public int getCargoWeight() { + return + this.cars + .stream() + .filter(car -> car instanceof CargoCar) + .map(car -> (CargoCar) car) + .map(ccar -> ccar.getCargoWeight()) + .reduce(0, (a, b) -> a+b); + } + + @Override + public String toString() { + return + "\uD83D\uDE82 - " + + this.cars + .stream() + .map(car -> car.toString()) + .collect(Collectors.joining(" - ")); + } +} diff --git a/src/main/java/inheritance/TrainCar.java b/src/main/java/inheritance/TrainCar.java new file mode 100644 index 0000000..22d64a5 --- /dev/null +++ b/src/main/java/inheritance/TrainCar.java @@ -0,0 +1,34 @@ +package inheritance; + + +public class TrainCar { + private int deadWeight; + + public TrainCar(int deadWeight) { + this.setDeadWeight(deadWeight); + } + + public int getTotalWeight() { + return deadWeight; + } + + public void setDeadWeight(int deadWeight) { + if (deadWeight < 0) + throw new IllegalArgumentException("Deadweight cannot be less than 0"); + this.deadWeight = deadWeight; + } + + public int getDeadWeight() { + return this.deadWeight; + } + + @Override + public String toString() { + return String.format( + "(Car w:%d)", + this.getTotalWeight() + ); + } +} + + diff --git a/src/main/java/patterns/observable/HighscoreList.java b/src/main/java/patterns/observable/HighscoreList.java new file mode 100644 index 0000000..3ceda8c --- /dev/null +++ b/src/main/java/patterns/observable/HighscoreList.java @@ -0,0 +1,63 @@ +package patterns.observable; + +import java.util.ArrayList; +import java.util.List; + +class HighscoreList { + + private int maxSize; + private List results; + private List listeners; + + public HighscoreList(int maxSize) { + this.maxSize = maxSize; + this.results = new ArrayList<>(); + this.listeners = new ArrayList<>(); + } + + public int size() { + return results.size(); + } + + public int getElement(int n) { + return results.get(n); + } + + public void addResult(int result) { + + int i = 0; + + if (results.isEmpty()) + results.add(result); + + else { + while (i + 1 < results.size() && results.get(i) <= result) + i++; + + if (i + 1 == results.size()) + results.add(result); + else + results.add(i, result); + } + + if (results.size() > maxSize) + results.remove(maxSize); + + System.out.println(results); + System.out.println(i); + + boolean elementWasDeleted = i + 1 == this.maxSize; + + for (var listener : listeners) + listener.listChanged(this, elementWasDeleted ? -1 : i); + + } + + public void addHighscoreListListener(HighscoreListListener listener) { + this.listeners.add(listener); + } + + public void removeHighscoreListListener(HighscoreListListener listener) { + this.listeners.remove(listener); + } +} \ No newline at end of file diff --git a/src/main/java/patterns/observable/HighscoreListListener.java b/src/main/java/patterns/observable/HighscoreListListener.java new file mode 100644 index 0000000..10cb425 --- /dev/null +++ b/src/main/java/patterns/observable/HighscoreListListener.java @@ -0,0 +1,5 @@ +package patterns.observable; + +interface HighscoreListListener { + void listChanged(HighscoreList list, int n); +} \ No newline at end of file diff --git a/src/main/java/patterns/observable/HighscoreListProgram.java b/src/main/java/patterns/observable/HighscoreListProgram.java new file mode 100644 index 0000000..fbe4195 --- /dev/null +++ b/src/main/java/patterns/observable/HighscoreListProgram.java @@ -0,0 +1,37 @@ +package patterns.observable; + +import java.util.Scanner; + +public class HighscoreListProgram implements HighscoreListListener { + + private HighscoreList list; + + /** oppretter en ny HighscoreList og registrerer seg selv (altså HighscoreListProgram-instansen) som lytter */ + public void init() { + this.list = new HighscoreList(4); + + this.list.addHighscoreListListener(this); + } + + /** leser inn tall (resultater) fra konsollet og legger dem til i listen */ + public void run() { + try(Scanner scanner = new Scanner(System.in)){ + var i = scanner.nextInt(); + // this.list.addResult(i); + System.out.println(i); + } + } + + /** observerer endringer i HighscoreList-instansen og skriver ut posisjonsargumentet, samt selve listen, til konsollen. */ + public void listChanged(HighscoreList list, int i) { + System.out.println(list.toString() + " " + i); + } + + public static void main(String[] args) { + HighscoreListProgram prog = new HighscoreListProgram(); + prog.init(); + while (true) { + prog.run(); + } + } +} diff --git a/src/test/java/inheritance/CargoCarTest.java b/src/test/java/inheritance/CargoCarTest.java new file mode 100644 index 0000000..d4b0e1d --- /dev/null +++ b/src/test/java/inheritance/CargoCarTest.java @@ -0,0 +1,25 @@ +package inheritance; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class CargoCarTest { + + private CargoCar cc; + + @BeforeEach + public void setup() { + cc = new CargoCar(3000, 2000); + } + + @Test + @DisplayName("Sjekke totalvekt") + public void testWeight() { + Assertions.assertEquals(5000, cc.getTotalWeight(), "Totalvekt ble feil etter initialisering"); + cc.setCargoWeight(4000); + Assertions.assertEquals(7000, cc.getTotalWeight(), "Totalvekt ble feil etter endret cargo-vekt"); + Assertions.assertEquals(4000, cc.getCargoWeight(), "Cargo-vekt ble feil etter endring i cargo-vekt"); + } +} \ No newline at end of file diff --git a/src/test/java/inheritance/CreditAccountTest.java b/src/test/java/inheritance/CreditAccountTest.java new file mode 100644 index 0000000..9eed769 --- /dev/null +++ b/src/test/java/inheritance/CreditAccountTest.java @@ -0,0 +1,65 @@ +package inheritance; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class CreditAccountTest { + private CreditAccount sub; + private static double epsilon = 0.0005d; + + @BeforeEach + public void setUp() { + sub = new CreditAccount(10000.0); + } + + @Test + @DisplayName("Sjekk at innskudd fungerer som det skal") + void testDeposit() { + assertEquals(0.0, sub.getBalance(), epsilon, "Saldoen på kontoen ble feil"); + sub.deposit(10000.0); + assertEquals(10000.0, sub.getBalance(), epsilon, "Saldoen på kontoen ble feil"); + assertThrows(IllegalArgumentException.class, () -> { + sub.deposit(-10000.0); + }, "Negativt innskudd burde gitt utløst IllegalArugment-unntak!"); + assertEquals(10000.0, sub.getBalance(), epsilon, "Saldoen på kontoen ble feil"); + } + + @Test + @DisplayName("Sjekk at uttak fungerer som det skal") + void testWithdraw() { + sub.deposit(20000.0); + sub.withdraw(5000.0); + assertEquals(15000.0, sub.getBalance(), epsilon, "Saldoen på kontoen ble feil"); + assertThrows(IllegalArgumentException.class, () -> { + sub.withdraw(-10000.0); + }, "Negativt uttak burde gitt utløst IllegalArugment-unntak!"); + assertEquals(15000.0, sub.getBalance(), epsilon, "Saldoen på kontoen ble feil"); + sub.withdraw(20000.0); + assertEquals(-5000.0, sub.getBalance(), epsilon, "Saldoen på kontoen ble feil"); + assertThrows(IllegalStateException.class, () -> { + sub.withdraw(20000.0); + }, "Uttak på mer enn kredittgrense burde utløst IllegalState-unntak"); + } + + @Test + @DisplayName("Sjekk at kredittgrensen fungerer som den skal") + void testCreditLine() { + assertEquals(10000.0, sub.getCreditLine(), epsilon, "Kredittgrensen var feil"); + sub.setCreditLine(5000.0); + assertEquals(5000.0, sub.getCreditLine(), epsilon, "Kredittgrensen var feil"); + assertThrows(IllegalArgumentException.class, () -> { + sub.setCreditLine(-5000.0); + }, "Kan ikke ha negativ kredittgrense"); + assertEquals(5000.0, sub.getCreditLine(), epsilon, "Kredittgrensen var feil"); + sub.withdraw(4000.0); + assertThrows(IllegalStateException.class, () -> { + sub.setCreditLine(3000.0); + }, "Kan ikke sette kredittgrense som vil gi ugyldig saldo"); + assertEquals(-4000.0, sub.getBalance(), epsilon, "Saldoen var feil"); + assertEquals(5000.0, sub.getCreditLine(), epsilon, "Kredittgrensen var feil"); + } +} \ No newline at end of file diff --git a/src/test/java/inheritance/DebitAccountTest.java b/src/test/java/inheritance/DebitAccountTest.java new file mode 100644 index 0000000..29be5db --- /dev/null +++ b/src/test/java/inheritance/DebitAccountTest.java @@ -0,0 +1,45 @@ +package inheritance; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class DebitAccountTest { + private DebitAccount sub; + private static double epsilon = 0.0005d; + + @BeforeEach + public void setUp() { + sub = new DebitAccount(); + } + + @Test + @DisplayName("Sjekk at innskudd fungerer som det skal") + void testDeposit() { + assertEquals(0.0, sub.getBalance(), epsilon, "Saldoen på kontoen ble feil"); + sub.deposit(10000.0); + assertEquals(10000.0, sub.getBalance(), epsilon, "Saldoen på kontoen ble feil"); + assertThrows(IllegalArgumentException.class, () -> { + sub.deposit(-10000.0); + }, "Negativt innskudd burde gitt utløst IllegalArugment-unntak!"); + assertEquals(10000.0, sub.getBalance(), epsilon, "Saldoen på kontoen ble feil"); + } + + @Test + @DisplayName("Sjekk at uttak fungerer som det skal") + void testWithdraw() { + sub.deposit(20000.0); + sub.withdraw(5000.0); + assertEquals(15000.0, sub.getBalance(), epsilon, "Saldoen på kontoen ble feil"); + assertThrows(IllegalArgumentException.class, () -> { + sub.withdraw(-10000.0); + }, "Negativt uttak burde gitt utløst IllegalArugment-unntak!"); + assertEquals(15000.0, sub.getBalance(), epsilon, "Saldoen på kontoen ble feil"); + assertThrows(IllegalStateException.class, () -> { + sub.withdraw(20000.0); + }, "Uttak på mer enn saldo burde utløst IllegalState-unntak"); + } +} \ No newline at end of file diff --git a/src/test/java/inheritance/PassengerCarTest.java b/src/test/java/inheritance/PassengerCarTest.java new file mode 100644 index 0000000..cbe5acc --- /dev/null +++ b/src/test/java/inheritance/PassengerCarTest.java @@ -0,0 +1,27 @@ +package inheritance; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class PassengerCarTest { + + private PassengerCar pc; + + @BeforeEach + public void setup() { + pc = new PassengerCar(3000, 200); + } + + @Test + @DisplayName("Sjekke totalvekt med passasjerer") + public void testWeight() { + Assertions.assertEquals(3000 + (200 * 80), pc.getTotalWeight(), "Totalvekt ble feil etter initialisering"); + pc.setPassengerCount(100); + Assertions.assertEquals(3000 + (100 * 80), pc.getTotalWeight(), + "Totalvekt ble feil etter endret passasjer-antall"); + Assertions.assertEquals(100, pc.getPassengerCount(), + "Antall passasjerer ble feil etter kall av setPassengerCount"); + } +} \ No newline at end of file diff --git a/src/test/java/inheritance/SavingsAccount2Test.java b/src/test/java/inheritance/SavingsAccount2Test.java new file mode 100644 index 0000000..a100a3a --- /dev/null +++ b/src/test/java/inheritance/SavingsAccount2Test.java @@ -0,0 +1,53 @@ +package inheritance; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class SavingsAccount2Test { + private SavingsAccount2 sub; + private static double epsilon = 0.0005d; + + @BeforeEach + public void setUp() { + sub = new SavingsAccount2(1, 50.0); + } + + @Test + @DisplayName("Sjekk at innskudd fungerer som det skal") + void testDeposit() { + assertEquals(0.0, sub.getBalance(), epsilon, "Saldoen på kontoen ble feil"); + sub.deposit(10000.0); + assertEquals(10000.0, sub.getBalance(), epsilon, "Saldoen på kontoen ble feil"); + assertThrows(IllegalArgumentException.class, () -> { + sub.deposit(-10000.0); + }, "Negativt innskudd burde utløst IllegalArugment-unntak!"); + assertEquals(10000.0, sub.getBalance(), epsilon, "Saldoen på kontoen ble feil"); + } + + @Test + @DisplayName("Sjekk at uttak fungerer som det skal") + void testWithdraw() { + sub.deposit(20000.0); + sub.withdraw(5000.0); + assertEquals(15000.0, sub.getBalance(), epsilon, "Saldoen på kontoen ble feil"); + assertThrows(IllegalArgumentException.class, () -> { + sub.withdraw(-10000.0); + }, "Negativt uttak burde utløst IllegalArugment-unntak!"); + assertEquals(15000.0, sub.getBalance(), epsilon, "Saldoen på kontoen ble feil"); + assertThrows(IllegalStateException.class, () -> { + sub.withdraw(20000.0); + }, "Uttak på mer enn saldo burde utløst IllegalState-unntak"); + assertEquals(15000.0, sub.getBalance(), epsilon, "Saldoen på kontoen ble feil"); + sub.withdraw(10000.0); + assertEquals(4950.0, sub.getBalance(), epsilon, "Saldoen på kontoen ble feil etter at gebyret var trukket fra"); + assertThrows(IllegalStateException.class, () -> { + sub.withdraw(4930.0); + }, "Uttak på mer enn saldo + gebyr burde utløst IllegalState-unntak"); + assertEquals(4950.0, sub.getBalance(), epsilon, "Saldoen på kontoen ble feil etter at gebyret var trukket fra"); + } + +} \ No newline at end of file diff --git a/src/test/java/inheritance/TrainCarTest.java b/src/test/java/inheritance/TrainCarTest.java new file mode 100644 index 0000000..05cad65 --- /dev/null +++ b/src/test/java/inheritance/TrainCarTest.java @@ -0,0 +1,24 @@ +package inheritance; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class TrainCarTest { + + private TrainCar tc; + + @BeforeEach + public void setup() { + tc = new TrainCar(3000); + } + + @Test + @DisplayName("Sjekk at dødvekt er det samme som totalvekt") + public void testDeadWeight() { + Assertions.assertEquals(3000, tc.getTotalWeight(), "Totalvekt skulle vært samme som dødvekt"); + tc.setDeadWeight(5000); + Assertions.assertEquals(5000, tc.getTotalWeight(), "Totalvekt skulle vært samme som dødvekt"); + } +} \ No newline at end of file diff --git a/src/test/java/inheritance/TrainTest.java b/src/test/java/inheritance/TrainTest.java new file mode 100644 index 0000000..75546ec --- /dev/null +++ b/src/test/java/inheritance/TrainTest.java @@ -0,0 +1,73 @@ +package inheritance; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class TrainTest { + + private Train train; + private PassengerCar pc1, pc2; + private CargoCar cc1, cc2; + + @BeforeEach + public void setup() { + train = new Train(); + pc1 = new PassengerCar(2000, 200); + pc2 = new PassengerCar(1500, 100); + cc1 = new CargoCar(3000, 5000); + cc2 = new CargoCar(2500, 7000); + } + + @Test + @DisplayName("Sjekk at toget inneholder vogner etter at de er lagt til") + public void testAddCarToTrain() { + train.addTrainCar(pc1); + train.addTrainCar(pc2); + train.addTrainCar(cc1); + Assertions.assertTrue(train.contains(pc1), "Toget skulle inneholdt passasjervogn 1 etter at det er lagt til"); + Assertions.assertTrue(train.contains(pc2), "Toget skulle inneholdt passasjervogn 2 etter at det er lagt til"); + Assertions.assertTrue(train.contains(cc1), "Toget skulle inneholdt lastvogn 1 etter at det er lagt til"); + Assertions.assertFalse(train.contains(cc2), "Toget skulle inneholdt lastvogn 2 etter at det er lagt til"); + } + + @Test + @DisplayName("Sjekke totalvekt på toget etter å ha lagt til vogner") + public void testTotalTrainWeight() { + train.addTrainCar(pc1); + train.addTrainCar(cc1); + + Assertions.assertEquals(8000 + (2000 + (200 * 80)), train.getTotalWeight(), + "Togets totalvekt ble feil etter å ha lagt til to vogner"); + + train.addTrainCar(pc2); + + Assertions.assertEquals(8000 + (2000 + (200 * 80)) + (1500 + (100 * 80)), train.getTotalWeight(), + "Togets totalvekt ble feil etter å ha lagt til enda en passasjervogn"); + } + + @Test + @DisplayName("Sjekk total passajerantall etter å ha lagt til to passasjervogner") + public void testPassengerCount() { + train.addTrainCar(pc1); + train.addTrainCar(pc2); + Assertions.assertEquals(300, train.getPassengerCount(), + "Passasjerantall ble feil etter å ha lagt til to passasjervogner"); + train.addTrainCar(cc1); + Assertions.assertEquals(300, train.getPassengerCount(), + "Passasjerantall ble feil etter å ha lagt til en lastvogn"); + } + + @Test + @DisplayName("Sjekk lastvekt på toget etter å ha lagt til to lastvogner") + public void testCargoWeight() { + train.addTrainCar(cc1); + train.addTrainCar(cc2); + Assertions.assertEquals(12000, train.getCargoWeight(), "Lastvekt ble feil etter å ha lagt til to lastvogner"); + + train.addTrainCar(pc1); + Assertions.assertEquals(12000, train.getCargoWeight(), + "Lastvekt ble feil etter å ha lagt til en passasjervogn"); + } +} \ No newline at end of file diff --git a/src/test/java/patterns/observable/.gitignore b/src/test/java/patterns/observable/.gitignore new file mode 100644 index 0000000..4445c21 --- /dev/null +++ b/src/test/java/patterns/observable/.gitignore @@ -0,0 +1,8 @@ +/.ObservableHighscoreListTest.java._trace +/.HighscoreListTest.java._trace +/.ObservableListTest.java._trace +/.StockTest.java._trace +/.StockIndexTest.java._trace +/.StopWatchManagerTest.java._trace +/.StopWatchTest.java._trace +/.SmartStockTest.java._trace diff --git a/src/test/java/patterns/observable/HighscoreListTest.java b/src/test/java/patterns/observable/HighscoreListTest.java new file mode 100644 index 0000000..56adb7a --- /dev/null +++ b/src/test/java/patterns/observable/HighscoreListTest.java @@ -0,0 +1,140 @@ +package patterns.observable; + +import java.util.Arrays; +import java.util.List; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class HighscoreListTest{ + + private HighscoreList highscoreList; + private int pos1, pos2; + + private void checkHighscoreList(String contextMessage, HighscoreList list, List elements) { + Assertions.assertEquals(elements.size(), list.size(), contextMessage + " -> Teste lengden på highscore-listen"); + int i = 0; + for (int element:elements) { + Assertions.assertEquals(element, list.getElement(i), contextMessage + " -> Teste at element på plass " + i + " stemmer"); + i++; + } + } + + private void addResultWithListener(int pos, int element) { + pos1 = pos; + highscoreList.addResult(element); + + // Sjekke at posisjonen som ble endret er den samme som ble sendt til lytteren + Assertions.assertEquals(pos1, pos2, "La til " + element + " på posisjon " + pos + " -> Teste posisjonen mottatt av lytter"); + } + + @BeforeEach + public void setup() { + highscoreList = new HighscoreList(3); + pos1 = -1; + pos2 = -1; + } + + @Test + @DisplayName("Teste konstruktør") + public void testConstructor() { + Assertions.assertEquals(0, highscoreList.size(), "Teste initialisering av highscore-listen"); + } + + @Test + @DisplayName("Legge til resultater (enkel)") + public void testAddElementSimple() { + highscoreList.addResult(5); + checkHighscoreList("La til 5 i tom liste",highscoreList, Arrays.asList(5)); + + highscoreList.addResult(6); + checkHighscoreList("La til 6 i listen [5]",highscoreList, Arrays.asList(5, 6)); + + highscoreList.addResult(2); + checkHighscoreList("La til 2 i listen [5, 6]", highscoreList, Arrays.asList(2, 5, 6)); + } + + @Test + @DisplayName("Legge til resultater - listen blir for lang") + public void testAddElementMoreThanMax() { + highscoreList.addResult(5); + highscoreList.addResult(6); + highscoreList.addResult(2); + checkHighscoreList("La til 5, 6 og 2 i listen", highscoreList, Arrays.asList(2, 5, 6)); + + highscoreList.addResult(3); + checkHighscoreList("La til 3 i listen [2, 5, 6]", highscoreList, Arrays.asList(2, 3, 5)); + + highscoreList.addResult(7); + checkHighscoreList("La til 7 i listen [2, 3, 5]", highscoreList, Arrays.asList(2, 3, 5)); + } + + @Test + @DisplayName("Legge til to like elementer") + public void testAddElementDuplicate() { + highscoreList.addResult(5); + highscoreList.addResult(6); + highscoreList.addResult(2); + checkHighscoreList("La til 5, 6 og 2 i listen", highscoreList, Arrays.asList(2, 5, 6)); + + highscoreList.addResult(2); + checkHighscoreList("La til 2 i listen [2, 5, 6]", highscoreList, Arrays.asList(2, 2, 5)); + } + + @Test + @DisplayName("Teste lyttere (enkel)") + public void testListListenersSimple() { + // Mocke en lytter + HighscoreListListener listener = (list, pos) -> pos2 = pos; + highscoreList.addHighscoreListListener(listener); + + addResultWithListener(0, 5); + checkHighscoreList("La til 5 i listen []", highscoreList, Arrays.asList(5)); + + addResultWithListener(1, 6); + checkHighscoreList("La til 6 i listen [5]",highscoreList, Arrays.asList(5, 6)); + + addResultWithListener(0, 2); + checkHighscoreList("La til 2 i listen [5, 6]", highscoreList, Arrays.asList(2, 5, 6)); + } + + @Test + @DisplayName("Med lytter - listen blir for lang") + public void testListListenerMoreThanMax() { + // Mocke en lytter + HighscoreListListener listener = (list, pos) -> pos2 = pos; + highscoreList.addHighscoreListListener(listener); + + highscoreList.addResult(5); + highscoreList.addResult(6); + highscoreList.addResult(2); + checkHighscoreList("La til 5, 6 og 2 i listen",highscoreList, Arrays.asList(2, 5, 6)); + + addResultWithListener(1, 3); + checkHighscoreList("La til 3 i listen [2, 5, 6]", highscoreList, Arrays.asList(2, 3, 5)); + + // Nullstille pos2 siden neste element havner utenfor listen og blir dermed ikke oppdatert av seg selv og sendt til lytter + pos2 = -1; + addResultWithListener(-1, 7); + checkHighscoreList("La til 7 i listen [2, 3, 5]", highscoreList, Arrays.asList(2, 3, 5)); + } + + @Test + @DisplayName("Med lytter - to like elementer") + public void testListListenerDuplicate() { + // Mocke en lytter + HighscoreListListener listener = (list, pos) -> pos2 = pos; + highscoreList.addHighscoreListListener(listener); + + highscoreList.addResult(5); + highscoreList.addResult(6); + highscoreList.addResult(2); + checkHighscoreList("La til 5, 6 og 2 i listen",highscoreList, Arrays.asList(2, 5, 6)); + + addResultWithListener(1, 2); + checkHighscoreList("La til 2 i listen [2, 5, 6]", highscoreList, Arrays.asList(2, 2, 5)); + } + +} \ No newline at end of file diff --git a/src/test/java/patterns/observable/ObservableHighscoreListTest.java b/src/test/java/patterns/observable/ObservableHighscoreListTest.java new file mode 100644 index 0000000..9a957f8 --- /dev/null +++ b/src/test/java/patterns/observable/ObservableHighscoreListTest.java @@ -0,0 +1,143 @@ +package patterns.observable; + +import java.util.Arrays; +import java.util.List; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class ObservableHighscoreListTest { + + private ObservableHighscoreList highscoreList; + private int pos1, pos2; + + private void checkHighscoreList(String contextMessage, ObservableHighscoreList list, List elements) { + Assertions.assertEquals(elements.size(), list.size(), + contextMessage + " -> Lengden på highscore-listen ble feil"); + int i = 0; + for (int element : elements) { + Assertions.assertEquals(element, list.getElement(i), + contextMessage + String.format(" -> Elementet på plass %d ble feil", i)); + i++; + } + } + + private void addResultWithListener(int pos, int element) { + pos1 = pos; + highscoreList.addResult(element); + + // Sjekke at posisjonen som ble endret er den samme som ble sendt til lytteren + Assertions.assertEquals(pos1, pos2, "La til " + element + " på posisjon " + pos + + "Posisjonen som ble endret var ikke samme som ble sendt til lytteren"); + } + + @BeforeEach + public void setup() { + highscoreList = new ObservableHighscoreList(3); + pos1 = -1; + pos2 = -1; + } + + @Test + @DisplayName("Teste konstruktør") + public void testConstructor() { + Assertions.assertEquals(0, highscoreList.size(), "Highscorelist ble ikke initialisert til en tom liste"); + } + + @Test + @DisplayName("Legge til resultater (enkel)") + public void testAddElementSimple() { + highscoreList.addResult(5); + checkHighscoreList("La til 5 i tom liste", highscoreList, Arrays.asList(5)); + + highscoreList.addResult(6); + checkHighscoreList("La til 6 i listen [5]", highscoreList, Arrays.asList(5, 6)); + + highscoreList.addResult(2); + checkHighscoreList("La til 2 i listen [5, 6]", highscoreList, Arrays.asList(2, 5, 6)); + } + + @Test + @DisplayName("Legge til resultater - listen blir for lang") + public void testAddElementMoreThanMax() { + highscoreList.addResult(5); + highscoreList.addResult(6); + highscoreList.addResult(2); + checkHighscoreList("La til 5, 6 og 2 i listen", highscoreList, Arrays.asList(2, 5, 6)); + + highscoreList.addResult(3); + checkHighscoreList("La til 3 i listen [2, 5, 6]", highscoreList, Arrays.asList(2, 3, 5)); + + highscoreList.addResult(7); + checkHighscoreList("La til 7 i listen [2, 3, 5]", highscoreList, Arrays.asList(2, 3, 5)); + } + + @Test + @DisplayName("Legge til to like elementer") + public void testAddElementDuplicate() { + highscoreList.addResult(5); + highscoreList.addResult(6); + highscoreList.addResult(2); + checkHighscoreList("La til 5, 6 og 2 i listen", highscoreList, Arrays.asList(2, 5, 6)); + + highscoreList.addResult(2); + checkHighscoreList("La til 2 i listen [2, 5, 6]", highscoreList, Arrays.asList(2, 2, 5)); + } + + @Test + @DisplayName("Teste lyttere (enkel)") + public void testListListenersSimple() { + // Mocke en lytter + ObservableListListener listener = (list, pos) -> pos2 = pos; + highscoreList.addObservableListListener(listener); + + addResultWithListener(0, 5); + checkHighscoreList("La til 5 i listen []", highscoreList, Arrays.asList(5)); + + addResultWithListener(1, 6); + checkHighscoreList("La til 6 i listen [5]", highscoreList, Arrays.asList(5, 6)); + + addResultWithListener(0, 2); + checkHighscoreList("La til 2 i listen [5, 6]", highscoreList, Arrays.asList(2, 5, 6)); + } + + @Test + @DisplayName("Med lytter - listen blir for lang") + public void testListListenerMoreThanMax() { + // Mocke en lytter + ObservableListListener listener = (list, pos) -> pos2 = pos; + highscoreList.addObservableListListener(listener); + + highscoreList.addResult(5); + highscoreList.addResult(6); + highscoreList.addResult(2); + checkHighscoreList("La til 5, 6 og 2 i listen", highscoreList, Arrays.asList(2, 5, 6)); + + addResultWithListener(1, 3); + checkHighscoreList("La til 3 i listen [2, 5, 6]", highscoreList, Arrays.asList(2, 3, 5)); + + // Nullstille pos2 siden neste element havner utenfor listen og blir dermed ikke + // oppdatert av seg selv og sendt til lytter + pos2 = -1; + addResultWithListener(-1, 7); + checkHighscoreList("La til 7 i listen [2, 3, 5]", highscoreList, Arrays.asList(2, 3, 5)); + } + + @Test + @DisplayName("Med lytter - to like elementer") + public void testListListenerDuplicate() { + // Mocke en lytter + ObservableListListener listener = (list, pos) -> pos2 = pos; + highscoreList.addObservableListListener(listener); + + highscoreList.addResult(5); + highscoreList.addResult(6); + highscoreList.addResult(2); + checkHighscoreList("La til 5, 6 og 2 i listen", highscoreList, Arrays.asList(2, 5, 6)); + + addResultWithListener(1, 2); + checkHighscoreList("La til 2 i listen [2, 5, 6]", highscoreList, Arrays.asList(2, 2, 5)); + } +} \ No newline at end of file diff --git a/src/test/java/patterns/observable/ObservableListTest.java b/src/test/java/patterns/observable/ObservableListTest.java new file mode 100644 index 0000000..c26457a --- /dev/null +++ b/src/test/java/patterns/observable/ObservableListTest.java @@ -0,0 +1,94 @@ +package patterns.observable; + +import java.util.Arrays; +import java.util.List; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class ObservableListTest { + + private ObservableList observableList; + private int pos1, pos2; + + private void checkObservableList(ObservableList list, List elements, String contextMessage) { + Assertions.assertEquals(elements.size(), list.size(), + contextMessage + " -> Lengden på ObservableList ble feil"); + + int i = 0; + for (int element : elements) { + Assertions.assertEquals(element, list.getElement(i), + contextMessage + String.format(" -> Element på plass %d var feil", i)); + i++; + } + } + + private void addElementWithListener(int pos, int element) { + pos1 = pos; + observableList.addElement(pos, element); + + // Sjekke at posisjonen som ble endret er den samme som ble sendt til lytteren + Assertions.assertEquals(pos1, pos2, + "La til " + element + " på posisjon " + pos + "posisjonen mottat av lytter ble feil, den var " + pos2); + } + + @BeforeEach + public void setup() { + observableList = new ObservableList() { + @Override + public boolean acceptsElement(final Object element) { + return (element instanceof Integer); + } + }; + pos1 = -1; + pos2 = -1; + } + + @Test + @DisplayName("Test konstruktør") + public void testConstructor() { + Assertions.assertEquals(0, observableList.size()); + } + + @Test + @DisplayName("Sjekk at listen aksepterer riktige elementer") + public void testAcceptsElement() { + Assertions.assertTrue(observableList.acceptsElement(5), "Listen skulle akseptert integers"); + Assertions.assertFalse(observableList.acceptsElement("5"), "Listen skulle ikke akseptert strenger"); + + Assertions.assertThrows(IllegalArgumentException.class, () -> { + observableList.addElement("5"); + }, "Listen skulle utløst et IllegalArgument-unntak når man prøver å legge til en streng"); + } + + @Test + @DisplayName("Teste å legge til elementer") + public void testAddElement() { + observableList.addElement(5); + checkObservableList(observableList, Arrays.asList(5), "La til 5 i tom liste"); + + observableList.addElement(6); + checkObservableList(observableList, Arrays.asList(5, 6), "La til 6 i listen [5]"); + + observableList.addElement(0, 2); + checkObservableList(observableList, Arrays.asList(2, 5, 6), "La til 2 på posisjon 0 i listen [5, 6]"); + } + + @Test + @DisplayName("Teste lytter") + public void testListListener() { + ObservableListListener listener = (list, pos) -> pos2 = pos; + observableList.addObservableListListener(listener); + + addElementWithListener(0, 5); + checkObservableList(observableList, Arrays.asList(5), "La til 5 i listen []"); + + addElementWithListener(1, 6); + checkObservableList(observableList, Arrays.asList(5, 6), "La til 6 i listen [5]"); + + addElementWithListener(0, 2); + checkObservableList(observableList, Arrays.asList(2, 5, 6), "La til 2 i listen [5, 6]"); + } +} \ No newline at end of file diff --git a/src/test/java/patterns/observable/SmartStockTest.java b/src/test/java/patterns/observable/SmartStockTest.java new file mode 100644 index 0000000..6ef59ca --- /dev/null +++ b/src/test/java/patterns/observable/SmartStockTest.java @@ -0,0 +1,111 @@ +package patterns.observable; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class SmartStockTest{ + + private SmartStock stock; + + private double oldPrice, newPrice; + + // Brukes for å sjekke at lyttere funker + private double oldPriceListener, newPriceListener; + + private void setPriceForListener(double oldPrice, double newPrice) { + oldPriceListener = oldPrice; + newPriceListener = newPrice; + } + + private void setPriceCheckListener(String contextMessage, double newPrice, double expectedOldPrice, double expectedNewPrice) { + // Oppdatere prisen + this.oldPrice = this.newPrice; + this.newPrice = newPrice; + stock.setPrice(newPrice); + + // Sjekke at lytter har mottatt endring + Assertions.assertEquals(expectedOldPrice, this.oldPriceListener, contextMessage + " -> Teste gammel pris for lytter etter å ha oppdatert pris fra " + oldPrice + " til " + newPrice); + Assertions.assertEquals(expectedNewPrice, this.newPriceListener, contextMessage + " -> Teste ny pris for lytter etter å ha oppdatert pris fra " + oldPrice + " til " + newPrice); + } + + @BeforeEach + public void setup() { + stock = new SmartStock("APPL", 110.0); + } + + @Test + @DisplayName("Teste kontruktør") + public void testConstructor() { + Assertions.assertEquals("APPL", stock.getTicker(), "Teste ticker"); + Assertions.assertEquals(110.0, stock.getPrice(), "Teste aksjeprisen"); + } + + @Test + @DisplayName("Negativ aksjepris gir feilmelding") + public void testSetNegativePrice() { + Assertions.assertThrows(IllegalArgumentException.class, () -> { + stock.setPrice(-20.0); + }, "Teste å sette negativ aksjepris"); + } + + @Test + @DisplayName("Aksjepris lik null gir feilmelding") + public void testSetZeroPrice() { + Assertions.assertThrows(IllegalArgumentException.class, () -> { + stock.setPrice(0); + }, "Teste å sette aksjepris lik null"); + } + + @Test + @DisplayName("Legge til lytter") + public void testStockListener() { + StockListener listener = (Stock stock, double oldPrice, double newPrice) -> setPriceForListener(oldPrice, newPrice); + stock.addStockListener(listener); + + setPriceCheckListener("Lytter på alt", 118.0, 110.0, 118.0); + Assertions.assertEquals(118.0, stock.getPrice(), "Teste aksjepris etter å ha oppdatert pris"); + + setPriceCheckListener("Lytter på alt", 121.0, 118.0, 121.0); + Assertions.assertEquals(121.0, stock.getPrice(), "Teste aksjepris etter å ha oppdatert pris 2 ganger"); + } + + @Test + @DisplayName("Teste lytter på prisintervall") + public void testIntervalListener() { + StockListener listener = (Stock stock, double oldPrice, double newPrice) -> setPriceForListener(oldPrice, newPrice); + stock.addStockListener(listener, 110.0, 120.0); + + // Pris innenfor intervallet gir ingen beskjed til lytter + setPriceCheckListener("Lytter på prisintervall", 118.0, 0.0, 0.0); + Assertions.assertEquals(118.0, stock.getPrice(), "Teste aksjepris etter å ha oppdatert pris"); + + // Pris utenfor intervallet gir beskjed til lytter + setPriceCheckListener("Lytter på prisintervall",121.0, 118.0, 121.0); + Assertions.assertEquals(121.0, stock.getPrice(), "Teste aksjepris etter å ha oppdatert pris for andre gang"); + + // Pris innenfor intervallet gir ingen beskjed til lytter (forventende verdier forblir det de var) + setPriceCheckListener("Lytter på prisintervall",115.0, 118.0, 121.0); + Assertions.assertEquals(115.0, stock.getPrice(), "Teste aksjepris etter å ha oppdatert pris for tredje gang"); + } + + @Test + @DisplayName("Teste lytter på differanse") + public void testDifferenceListener() { + StockListener listener = (Stock stock, double oldPrice, double newPrice) -> setPriceForListener(oldPrice, newPrice); + stock.addStockListener(listener, 10.0); + + // Pris med differanse mindre enn 10 varsler ikke lytter + setPriceCheckListener("Lytter på differanse",118.0, 0.0, 0.0); + Assertions.assertEquals(118.0, stock.getPrice()); + + // Pris med differanse større enn 10 varsler lytter + setPriceCheckListener("Lytter på differanse",121.0, 110.0, 121.0); + Assertions.assertEquals(121.0, stock.getPrice()); + + // Pris med differanse mindre enn 10 varsler ikke lytter (forventende verdier forblir det de var) + setPriceCheckListener("Lytter på differanse", 115.0, 110.0, 121.0); + Assertions.assertEquals(115.0, stock.getPrice()); + } +} \ No newline at end of file diff --git a/src/test/java/patterns/observable/StockIndexTest.java b/src/test/java/patterns/observable/StockIndexTest.java new file mode 100644 index 0000000..38cb8bd --- /dev/null +++ b/src/test/java/patterns/observable/StockIndexTest.java @@ -0,0 +1,84 @@ +package patterns.observable; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class StockIndexTest{ + + private static final double facebookPrice = 67.80d; + private static final double applePrice = 534.98; + private static final double epsilon = 0.000001d; + + private Stock facebook, apple; + private StockIndex index0, index1, indexN; + + @BeforeEach + public void setup() { + facebook = new Stock("FB", facebookPrice); + apple = new Stock("AAPL", applePrice); + + index0 = new StockIndex("OSEBX"); + index1 = new StockIndex("OSEBX", facebook); + indexN = new StockIndex("OSEBX", facebook, apple); + } + + @Test + @DisplayName("Teste konstruktør") + public void testConstructor() { + Assertions.assertEquals(0.0, index0.getIndex(), epsilon, "Teste verdien til indeks med 0 aksjer "); + Assertions.assertEquals(facebookPrice, index1.getIndex(), epsilon, "Teste verdien til indeks med 1 aksje"); + Assertions.assertEquals(facebookPrice + applePrice, indexN.getIndex(), epsilon, "Teste verdien til indeks med 2 aksjer"); + } + + @Test + @DisplayName("Legge til aksje") + public void testAddStock() { + Assertions.assertEquals(0.0, index0.getIndex(), epsilon, "Teste verdien til indeks med 0 aksjer"); + index0.addStock(facebook); + Assertions.assertEquals(facebookPrice, index0.getIndex(), epsilon, "Teste verdien til indeks etter å ha lagt til 1 aksje"); + } + + @Test + @DisplayName("Legge til samme aksje to ganger") + public void testAddDuplicateStocks() { + Assertions.assertEquals(0.0, index0.getIndex(), epsilon, "Teste verdien til indeks med 0 aksjer"); + + index0.addStock(facebook); + Assertions.assertEquals(facebookPrice, index0.getIndex(), epsilon, "Teste verdien til indeks etter å ha lagt til 1 aksje"); + + index0.addStock(facebook); + Assertions.assertEquals(facebookPrice, index0.getIndex(), epsilon, "Teste verdien til indeks etter å ha lagt til aksje som allerede er med i indeks"); + } + + @Test + @DisplayName("Fjerne aksje") + public void testRemoveStock() { + Assertions.assertEquals(facebookPrice + applePrice, indexN.getIndex(), epsilon, "Teste verdien til indeks med 2 aksjer"); + + indexN.removeStock(apple); + Assertions.assertEquals(facebookPrice, indexN.getIndex(), epsilon, "Teste verdien til indeks etter å ha fjernet 1 aksje"); + + indexN.removeStock(apple); + Assertions.assertEquals(facebookPrice, indexN.getIndex(), epsilon, "Teste verdien til indeks etter å ha fjernet 1 aksje som ikke var med i indeks"); + + indexN.removeStock(facebook); + Assertions.assertEquals(0.0, indexN.getIndex(), epsilon, "Teste verdien til indeks etter å ha fjernet eneste aksje i indeks"); + } + + @Test + @DisplayName("Endre aksjepris") + public void testChangePrice() { + double facebookPrice2 = 67.0; + double facebookPrice3 = 69.0; + + facebook.setPrice(facebookPrice2); + Assertions.assertEquals(facebookPrice2, index1.getIndex(), epsilon, "Teste verdien til indeks med 1 aksje etter å ha endret prisen på aksje"); + Assertions.assertEquals(facebookPrice2 + applePrice, indexN.getIndex(), epsilon, "Teste verdien til indeks med 2 aksjer etter å ha endret prisen til 1 av aksjene"); + + facebook.setPrice(facebookPrice3); + Assertions.assertEquals(facebookPrice3, index1.getIndex(), epsilon, "Teste verdien til indeks med 1 aksje etter å ha endret prisen på aksje for andre gang"); + Assertions.assertEquals(facebookPrice3 + applePrice, indexN.getIndex(), epsilon, "Teste verdien til indeks med 2 aksjer etter å ha endret prisen til 1 av aksjene for andre gang"); + } +} \ No newline at end of file diff --git a/src/test/java/patterns/observable/StockTest.java b/src/test/java/patterns/observable/StockTest.java new file mode 100644 index 0000000..e0bffd2 --- /dev/null +++ b/src/test/java/patterns/observable/StockTest.java @@ -0,0 +1,74 @@ +package patterns.observable; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class StockTest{ + + private Stock stock; + private double oldPrice, newPrice; + + // Brukes for å sjekke at lyttere funker + private double oldPriceListener, newPriceListener; + + private void setPriceForListener(double oldPrice, double newPrice) { + oldPriceListener = oldPrice; + newPriceListener = newPrice; + } + + private void setPriceCheckListener(double newPrice, double expectedOldPrice, double expectedNewPrice) { + // Oppdatere prisen + this.oldPrice = this.newPrice; + this.newPrice = newPrice; + stock.setPrice(newPrice); + + // Sjekke at lytter har mottatt endring + Assertions.assertEquals(expectedOldPrice, this.oldPriceListener, "Teste gammel pris for lytter etter å ha oppdatert pris fra " + oldPrice + " til " + newPrice); + Assertions.assertEquals(expectedNewPrice, this.newPriceListener, "Teste ny pris for lytter etter å ha oppdatert pris fra " + oldPrice + " til " + newPrice); + } + + @BeforeEach + public void setup() { + stock = new Stock("APPL", 110.0); + oldPrice = 0.0; newPrice = 110.0; + oldPriceListener = 0.0; newPriceListener = 0.0; + } + + @Test + @DisplayName("Teste kontruktør") + public void testConstructor() { + Assertions.assertEquals("APPL", stock.getTicker(), "Teste ticker"); + Assertions.assertEquals(110.0, stock.getPrice(), "Teste aksjeprisen"); + } + + @Test + @DisplayName("Negativ aksjepris gir feilmelding") + public void testSetNegativePrice() { + Assertions.assertThrows(IllegalArgumentException.class, () -> { + stock.setPrice(-20.0); + }, "Teste å sette negativ aksjepris"); + } + + @Test + @DisplayName("Aksjepris lik null gir feilmelding") + public void testSetZeroPrice() { + Assertions.assertThrows(IllegalArgumentException.class, () -> { + stock.setPrice(0); + }, "Teste å sette aksjepris lik null"); + } + + @Test + @DisplayName("Legge til lytter") + public void testStockListener() { + StockListener listener = (Stock stock, double oldPrice, double newPrice) -> setPriceForListener(oldPrice, newPrice); + stock.addStockListener(listener); + + setPriceCheckListener(118.0, 110.0, 118.0); + Assertions.assertEquals(118.0, stock.getPrice(), "Teste aksjepris etter å ha oppdatert pris"); + + setPriceCheckListener(121.0, 118.0, 121.0); + Assertions.assertEquals(121.0, stock.getPrice(), "Teste aksjepris etter å ha oppdatert pris 2 ganger"); + } +}