master
Oystein Kristoffer Tveit 2021-08-29 20:36:32 +02:00
parent acf2b751a5
commit 6ea11e55c1
50 changed files with 1856 additions and 9 deletions

3
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"java.configuration.updateBuildConfiguration": "automatic"
}

View File

@ -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 |
| [Ø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 |

View File

@ -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: `<prosjektnavn>/.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.

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

View File

@ -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

View File

@ -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 <code>internalWithdraw(<i>uttaksbeløp</i>)</code>,
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).

View File

@ -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).

View File

@ -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`:
<img src="images/ObservableList_del1.png" width="570">
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:
<img src="images/ObservableList_del2.png" width="670">
### 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.
<img src="images/ObservableList_del3.png" width="670">
Testkode for denne oppgaven finner du her: [patterns/observable/ObservableListTest.java](../../src/test/java/patterns/observable/ObservableListTest.java).

View File

@ -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.

View File

@ -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).

View File

@ -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
<img src="images/Train_del1.png" width="170">
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
<img src="images/Train_del2.png" width="350">
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
<img src="images/Train_del3.png" width="450">
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).

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

View File

@ -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;
}
}

View File

@ -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()
);
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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()
);
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,55 @@
package inheritance;
import java.util.ArrayList;
import java.util.Collection;
import java.util.stream.Collectors;
public class Train {
private Collection<TrainCar> 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(" - "));
}
}

View File

@ -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()
);
}
}

View File

@ -0,0 +1,63 @@
package patterns.observable;
import java.util.ArrayList;
import java.util.List;
class HighscoreList {
private int maxSize;
private List<Integer> results;
private List<HighscoreListListener> 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);
}
}

View File

@ -0,0 +1,5 @@
package patterns.observable;
interface HighscoreListListener {
void listChanged(HighscoreList list, int n);
}

View File

@ -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();
}
}
}

View File

@ -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");
}
}

View File

@ -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");
}
}

View File

@ -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");
}
}

View File

@ -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");
}
}

View File

@ -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");
}
}

View File

@ -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");
}
}

View File

@ -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");
}
}

View File

@ -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

View File

@ -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<Integer> 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));
}
}

View File

@ -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<Integer> 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));
}
}

View File

@ -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<Integer> 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]");
}
}

View File

@ -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());
}
}

View File

@ -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");
}
}

View File

@ -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");
}
}