Compare commits

...

13 Commits

Author SHA1 Message Date
3f7ea46a2f oppgave: train 2026-03-23 18:36:37 +01:00
fd661adfc5 oppgave: abstractaccount 2026-03-23 17:54:35 +01:00
fe2266656f oppgave: savingsaccount 2026-03-20 14:48:22 +01:00
c79555e133 move tests for tasks not implemented 2026-03-17 11:31:51 +01:00
285b61aa02 Merge branch 'main' 2026-03-17 11:20:10 +01:00
Andreas Omholt Olsen
2d0a64006f fix: savingsaccount tests exception type 2026-03-13 14:39:39 +01:00
Andreas Omholt Olsen
daa94740c8 fix: Classnames in abstractaccount 2026-03-13 14:37:01 +01:00
Mathias
2cc172d1f6 fix: renamed train files 2026-03-09 10:50:44 +01:00
Andreas Omholt Olsen
18bc153848 Merge pull request #8 from tdt4100/add-oving7
Add oving 7
2026-03-06 11:01:52 +01:00
Andreas Omholt Olsen
1deb0cc650 Add oving 7 2026-03-06 10:59:33 +01:00
04a598a5fa oppgave: logger 2026-03-03 06:31:50 +01:00
341848f3e8 oppgave: named 2026-03-03 05:26:25 +01:00
00f8009e0d oppgave: highscorelist 2026-03-03 04:22:55 +01:00
68 changed files with 2497 additions and 23 deletions

View File

@@ -11,3 +11,4 @@ Denne mappen inneholder øvingstekster for TDT4100 - Objektorientert programmeri
| [Øving 4](./oppgavetekster/oving4/README.md) | Objektstrukturer |
| [Øving 5](./oppgavetekster/oving5/README.md) | Grensesnitt |
| [Øving 6](./oppgavetekster/oving6/README.md) | Observatør-observert og delegering |
| [Øving 7](./oppgavetekster/oving7/README.md) | Arv og abstrakte klasser |

View File

@@ -0,0 +1,41 @@
# 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.
Denne oppgaven er på likt format som dere møtte i [Øving 3 - Card-oppgaven](../oving3/Card.md), der det ikke blir oppgitt en detaljert beskrivelse av klassene her i `README`-filen, men heller gjennom Javadoc-dokumentasjonen. Dette er igjen for å gi dere øving i å lese og forstå dokumentasjon, som er et vanlig format å bli gitt oppgaver på eksamen.
> Kjapt tips: Hold musen over metoden/klassen for å lese Javadoc-dokumentasjonen på et fint format.
Filene i denne oppgaven skal legges i [oving7/abstractaccount](../../src/main/java/oving7/abstractaccount).
## Del 1 - Abstrakt klasse AbstractAccount
En bank består av mange ulike type kontoer: sparekontoer, brukskontoer, depositumskontoer, støttekontoer, osv. 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.
Vær oppmerksom på at du fra og med [del 2](#del-2---debitaccount-extends-abstractaccount) 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.
Skjelletet til `AbstractAccount`-klassen finner du i [oving7/abstractaccount/AbstractAccount.java](../../src/main/java/oving7/abstractaccount/AbstractAccount.java).
Det er ingen spesifikke tester for denne klassen, siden abstrakte klasser ikke kan instansieres.
## Del 2 - DebitAccount extends AbstractAccount
Skjelettet til `DebitAccount`-klassen finner du i [oving7/abstractaccount/DebitAccount.java](../../src/main/java/oving7/abstractaccount/DebitAccount.java).
Testkode for oppgavene finner du her: [oving7/abstractaccount/DebitAccountTest.java](../../src/test/java/oving7/abstractaccount/DebitAccountTest.java).
## Del 3 - CreditAccount extends AbstractAccount
Skjelettet til `CreditAccount`-klassen finner du i [oving7/abstractaccount/CreditAccount.java](../../src/main/java/oving7/abstractaccount/CreditAccount.java).
Testkode for oppgavene finner du her: [oving7/abstractaccount/CreditAccountTest.java](../../src/test/java/oving7/abstractaccount/CreditAccountTest.java).
## Del 4 - SavingsAccount extends AbstractAccount
> Merk at denne klassen har samme navn som den fra [SavingsAccount-oppgaven](./SavingsAccount.md), men at dette er uproblematisk siden de befinner seg i forskjellige mapper.
Skjelettet til `SavingsAccount`-klassen finner du i [oving7/abstractaccount/SavingsAccount.java](../../src/main/java/oving7/abstractaccount/SavingsAccount.java).
Testkode for oppgavene finner du her: [oving7/abstractaccount/SavingsAccountTest.java](../../src/test/java/oving7/abstractaccount/SavingsAccountTest.java).

View File

@@ -0,0 +1,21 @@
# 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). Dersom du ikke har gjort `Card`-oppgavene allerede, bør du gjøre disse først. Hvis du ikke har gjort det, kan du kopiere koden fra [løsningsforslaget](https://git.ntnu.no/tdt4100/tdt4100-lf-25/blob/main/src/main/java/oving5/card), som kommer til å være tilgjengelig etter siste demonstrasjonsfrist for øving 5.
Filene i denne oppgaven skal legges i [oving7/card](../../src/main/java/oving7/card).
## 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: [oving7/card/CardDeckTest.java](../../src/test/java/oving7/card/CardDeckTest.java) og [oving7/card/CardHandTest.java](../../src/test/java/oving7/card/CardHandTest.java).

View File

@@ -0,0 +1,65 @@
# 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 listen og skriver ut listen hver gang et nytt resultat faktisk blir lagt til.
Klassene skal legges i [`src/main/java/oving7/observablelist`](../../src/main/java/oving7/observablelist) og tilhørende tester ligger i [`src/test/java/oving7/observablelist`](../../src/test/java/oving7/observablelist).
## Del 1: Implementasjon av ObservableList og ObservableHighscoreList
En `ObservableHighscoreList` skal holde styr på heltallsresultater (av typen `int`/`Integer`). Listen skal være _observerbar_ ved at den kan registrere lyttere (`ObservableListListener`-instanser) og si fra til dem når listen blir endret. Listen 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 listen 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 listen 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](#del-3-observablelist) og som da skal holde orden på listen. `ObservableList` vil ha en del generelle metoder som `ObservableHighscoreList` arver og kan bruke. For å kunne kjøre testene for `ObservableHighscoreList` allerede her i [del 1](#del-1-implementasjon-av-observablelist-og-observablehighscorelist), 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 listen skal kunne holde. Denne verdien må brukes av `addResult`, slik at resultater som er for dårlige kastes.
- `int size()` - returnerer antall elementer i listen, 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å listen, så legges det inn på riktig plass. Dersom listen blir for lang, så må dårligste resultat kastes. Alle registrerte lyttere må få beskjed om en evt. endring av listen, inkludert hvilken posisjon som ble endret.
- `void addObservableListListener(ObservableListListener)` - registrerer en ny lytter.
- `void removeObservableListListener(ObservableListListener)` - fjerner en tidligere registrert lytter.
Klassediagram for `HighscoreList`, `ListListener` og `ObservableList`:
![ObservableList_del1](./img/observablelist_del1.png)
Testkode for denne oppgaven finner du her: [oving7/observablelist/ObservableHighscoreListTest.java](../../src/test/java/oving7/observablelist/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 listen. 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:
![ObservableList_del2](./img/observablelist_del2.png)
## 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 listen (slik at listen 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 listen.
- `Object getElement(int)` - returnerer elementet i posisjonen angitt av argumentet.
- `abstract boolean acceptsElement(Object)` - returnerer hvorvidt _subklassen_ aksepterer at objektet legges inn i listen (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 listen, 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.
![ObservableList_del3](./img/observablelist_del3.png)
Testkode for denne oppgaven finner du her: [oving7/observablelist/ObservableListTest.java](../../src/test/java/oving7/observablelist/ObservableListTest.java).

View File

@@ -0,0 +1,46 @@
# Ø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 [`src/main/java/oving7`](../../src/main/java/oving7).
I begge delene er antageligvis vanskelighetsgraden stigende. Alle er høyst eksamensrelevante og det anbefales følgelig å ta en titt på samtlige.
For å få 2 poeng på øvingen må det gjennomføres til sammen *4* valgfrie oppgaver fra [del 1](#del-1-arv) og [del 2](#del-2-abstrakte-klasser-og-arv).
### Del 1: Arv
Velg og gjennomfør *minst én* av oppgavene om arv:
- [CardContainerImpl](./CardContainerImpl.md) (Lett) (Anbefalt)
- [Train](./Train.md) (Lett)
- [SavingsAccount](./SavingsAccount.md) (Middels)
### Del 2: Abstrakte klasser og arv
Velg og gjennomfør *minst én* av oppgavene om abstrakte klasser og arv:
- [AbstractAccount](./AbstractAccount.md) (Middels) (Anbefalt)
- [ObservableList](./ObservableList.md) (Vanskelig)
## 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/ntnu.no/spring2025/tdt4100).
## 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,63 @@
# 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`-oppgaven fra [øving 2](../oving2/Account.md).
Filene i denne oppgaven skal legges i [oving7/savingsaccount](../../src/main/java/oving7/savingsaccount).
## Del 1 - SavingsAccount implements Account
En bank består av mange ulike type kontoer: sparekontoer, brukskontoer, depositumskontoer, støttekontoer, osv. Felles for alle kontoer er `Account`-grensesnittet, som er definert under:
```java
package oving7.savingsaccount;
public interface Account {
void deposit(double amount);
void withdraw(double amount);
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.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](#del-2---bsu-extends-savingsaccount) og [del 3](#del-3---foreldrespar-extends-savingsaccount) 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.
> Merk at denne klassen har samme navn som den fra [AbstractAccount-oppgaven](./AbstractAccount.md), men at dette er uproblematisk siden de befinner seg i forskjellige mapper.
Testkode for oppgavene finner du her: [oving7/savingsaccount/SavingsAccountTest.java](../../src/test/java/oving7/savingsaccount/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. Med andre ord 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: [oving7/savingsaccount/BSUTest.java](../../src/test/java/oving7/savingsaccount/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: [oving7/savingsaccount/ForeldreSparTest.java](../../src/test/java/oving7/savingsaccount/ForeldreSparTest.java).

View File

@@ -0,0 +1,51 @@
# 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_.
Denne oppgaven er på likt format som dere møtte i [Øving 3 - Card-oppgaven](../oving3/Card.md), der det ikke blir oppgitt en detaljert beskrivelse av klassene her i `README`-filen, men heller gjennom Javadoc-dokumentasjonen. Dette er igjen for å gi dere øving i å lese og forstå dokumentasjon, som er et vanlig format å bli gitt oppgaver på eksamen.
> Kjapt tips: Hold musen over metoden/klassen for å lese Javadoc-dokumentasjonen på et fint format.
Filene i denne oppgaven skal legges i [oving7/train](../../src/main/java/oving7/train).
## Del 1 - TrainCar
![Train_del1](./img/train_del1.png)
I denne delen skal du lage en klasse kalt `TrainCar` for en enkel og generell togvogn, med følgende funksjonalitet som beskrevet i Javadoc-dokumentasjonen i klassen. Se også diagrammet over.
Skjellet for klassen finner du i [oving7/train/TrainCar.java](../../src/main/java/oving7/train/TrainCar.java).
Testkode for oppgaven finner du her: [oving7/train/TrainCarTest.java](../../src/test/java/oving7/train/TrainCarTest.java).
## Del 2 - CargoCar og PassengerCar
![Train_del2](./img/train_del2.png)
I denne delen skal du lage to forskjellige typer togvogner som er spesialiserte for sitt bruk. Begge arver fra `TrainCar`.
### CargoCar extends TrainCar
Denne klassen skal gjenspeile en lastevogn som frakter diverse ting og tang. Funksjonalitet skal være som beskrevet i dokumentasjonen. Se også diagrammet over.
Skjellet for klassen finner du i [oving7/train/CargoCar.java](../../src/main/java/oving7/train/CargoCar.java).
Testkode for oppgaven finner du her: [oving7/train/CargoCarTest.java](../../src/test/java/oving7/train/CargoCarTest.java).
### PassengerCar extends TrainCar
Denne klassen gjenspeiler en passasjervogn. Metodene skal være som beskrevet i dokumentasjonen. Se også diagrammet over.
Legg merke til at for å beregne totalvekta, så kan du anta at en gjennomsnittspassasjer veier 80 kg.
Skjellet for klassen finner du i [oving7/train/PassengerCar.java](../../src/main/java/oving7/train/PassengerCar.java).
Testkode for oppgaven finner du her: [oving7/train/PassengerCarTest.java](../../src/test/java/oving7/train/PassengerCarTest.java).
## Del 3 - Train
![Train_del3](./img/train_del3.png)
Klassen `Train` skal forestille et tog bestående av et sett vogner. Klassen skal ha metoder som beskrevet i dokumentasjonen. Se også diagrammet over.
Testkode for oppgaven finner du her: [oving7/train/TrainTest.java](../../src/test/java/oving7/train/TrainTest.java).

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

View File

@@ -0,0 +1,26 @@
@startuml observablelist_del1
skinparam dpi 400
class ObservableList
class ObservableHighscoreList {
- int maxSize
- List<Integer> results
+ ObservableHighscoreList(int)
+ int size()
+ int getElement(int)
+ void addResult(int)
+ void addObservableListListener(ObservableListListener)
+ void removeObservableListListener(ObservableListListener)
}
interface ObservableListListener {
void listChanged(ObservableList)
}
ObservableHighscoreList --u|> ObservableList
ObservableHighscoreList --> "observableListListeners: *" ObservableListListener
@enduml

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 KiB

View File

@@ -0,0 +1,34 @@
@startuml observablelist_del2
skinparam dpi 400
class ObservableList
class ObservableHighscoreList {
- int maxSize
- List<Integer> results
+ ObservableHighscoreList(int)
+ int size()
+ int getElement(int)
+ void addResult(int)
+ void addObservableListListener(ObservableListListener)
+ void removeObservableListListener(ObservableListListener)
}
interface ObservableListListener {
void listChanged(ObservableList)
}
class ObservableHighscoreListProgram {
- ObservableHighscoreList observableHighscoreList
+ void init()
+ void run()
}
ObservableHighscoreList --l|> ObservableList : "\t\t"
ObservableHighscoreList --> "observableListListeners: *" ObservableListListener
ObservableHighscoreListProgram ..r|> ObservableListListener : "\t\t"
@enduml

Binary file not shown.

After

Width:  |  Height:  |  Size: 343 KiB

View File

@@ -0,0 +1,44 @@
@startuml observablelist_del3
skinparam dpi 400
abstract class ObservableList {
- List<Object> elements
+ int size()
+ void addObservableListListener(ObservableListListener)
+ void removeObservableListListener(ObservableListListener)
+ Object getElement(int)
# {abstract} boolean acceptsElement(Object)
# void addElement(Object)
# void removeElement(Object)
}
class ObservableHighscoreList {
- int maxSize
- List<Integer> results
+ ObservableHighscoreList(int)
+ int size()
+ int getElement(int)
+ void addResult(int)
+ void addObservableListListener(ObservableListListener)
+ void removeObservableListListener(ObservableListListener)
}
interface ObservableListListener {
void listChanged(ObservableList)
}
class ObservableHighscoreListProgram {
- ObservableHighscoreList observableHighscoreList
+ void init()
+ void run()
}
ObservableHighscoreList --l|> ObservableList : "\t\t"
ObservableHighscoreList --> "observableListListeners: *" ObservableListListener
ObservableHighscoreListProgram ..r|> ObservableListListener : "\t\t"
@enduml

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

View File

@@ -0,0 +1,14 @@
@startuml train_del1
skinparam dpi 400
class TrainCar {
- int deadWeight
+ int getTotalWeight()
+ void setDeadWeight(int)
+ int getDeadWeight()
+ String toString()
}
@enduml

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 KiB

View File

@@ -0,0 +1,33 @@
@startuml train_del2
skinparam dpi 400
class TrainCar {
- int deadWeight
+ int getTotalWeight()
+ void setDeadWeight(int)
+ int getDeadWeight()
+ String toString()
}
class CargoCar {
- int cargoWeight
+ CargoCar(int, int)
+ void setCargoWeight(int)
+ int getCargoWeight()
}
class PassengerCar {
- int passengerCount
+ PassengerCar(int, int)
+ void setPassengerCount(int)
+ int getPassengerCount()
}
CargoCar --u|> TrainCar
PassengerCar --u|> TrainCar
@enduml

Binary file not shown.

After

Width:  |  Height:  |  Size: 287 KiB

View File

@@ -0,0 +1,44 @@
@startuml train_del3
skinparam dpi 400
class TrainCar {
- int deadWeight
+ int getTotalWeight()
+ void setDeadWeight(int)
+ int getDeadWeight()
+ String toString()
}
class CargoCar {
- int cargoWeight
+ CargoCar(int, int)
+ void setCargoWeight(int)
+ int getCargoWeight()
+ String toString()
}
class PassengerCar {
- int passengerCount
+ PassengerCar(int, int)
+ void setPassengerCount(int)
+ int getPassengerCount()
}
class Train {
+ void addTrainCar(TrainCar)
+ boolean contains(TrainCar)
+ int getTotalWeight()
+ int getPassengerCount()
+ int getCargoWeight()
+ String toString()
}
CargoCar --u|> TrainCar
PassengerCar --u|> TrainCar
Train --> "trainCars: *" TrainCar
@enduml

View File

@@ -1,25 +1,65 @@
package oving5;
import java.util.function.BinaryOperator;
import java.util.Stack;
import java.util.HashMap;
public class RPNCalc {
private HashMap<Character, BinaryOperator<Double>> ops = new HashMap<Character, BinaryOperator<Double>>();
private Stack<Double> stack = new Stack<>();
RPNCalc() {
}
public void performOperation(char c) {
boolean addOperator(char op, BinaryOperator<Double> f) {
if (ops.containsKey(op) || f == null) {
return false;
}
ops.put(op, f);
return true;
}
public void removeOperator(char c) {
void push(double n) {
stack.push(n);
}
public boolean addOperator(char c, BinaryOperator<Double> d) {
return false;
double pop() {
if (stack.isEmpty()) {
return Double.NaN;
}
return stack.pop();
}
public Double pop() {
return 0.0;
double peek(int i) {
int id = stack.size() - i;
if (id < 0 || id >= stack.size()) {
return Double.NaN;
}
return stack.get(id);
}
public void push(int i) {
int getSize() {
return stack.size();
}
void performOperation(char c) {
BinaryOperator<Double> op = ops.get(c);
if (op == null) {
throw new UnsupportedOperationException("Invalid operator: " + c);
}
if (stack.size() <= 1) {
return;
}
double a = stack.pop();
double b = stack.pop();
stack.push(op.apply(b, a));
}
void removeOperator(char c) {
ops.remove(c);
}
}

View File

@@ -0,0 +1,10 @@
package oving5.named;
public interface Named {
String getFullName();
String getFamilyName();
String getGivenName();
void setGivenName(String name);
void setFamilyName(String name);
void setFullName(String name);
}

View File

@@ -0,0 +1,14 @@
package oving5.named;
import java.util.Comparator;
public class NamedComparator implements Comparator<Named> {
@Override
public int compare(Named named1, Named named2) {
int a = named1.getFamilyName().compareTo(named2.getFamilyName());
if (a == 0) {
return named1.getGivenName().compareTo(named2.getGivenName());
}
return a;
}
}

View File

@@ -1,28 +1,43 @@
package oving5.named;
public class Person1 {
Person1(String s) {
public class Person1 implements Named {
private String givenName;
private String familyName;
private String fullName;
Person1(String givenName, String familyName) {
setFullName(givenName + " " + familyName);
setGivenName(givenName);
setFamilyName(familyName);
}
public void setFullName(String s) {
public void setFullName(String fullName) {
this.fullName = fullName;
String[] l = fullName.split(" ");
givenName = (l[0]);
familyName = (l[1]);
}
public void setFamilyName(String s) {
public void setFamilyName(String familyName) {
this.familyName = familyName;
setFullName(givenName + " " + familyName);
}
public void setGivenName(String s) {
public void setGivenName(String givenName) {
this.givenName = givenName;
setFullName(givenName + " " + familyName);
}
public String getFullName() {
return "";
return fullName;
}
public String getFamilyName() {
return "";
return familyName;
}
public String getGivenName() {
return "";
return givenName;
}
}

View File

@@ -1,28 +1,41 @@
package oving5.named;
public class Person2 {
Person2(String s) {
public class Person2 implements Named {
private String fullName;
private String familyName;
private String givenName;
Person2(String fullName) {
setFullName(fullName);
}
public void setFullName(String s) {
public void setFullName(String fullName) {
this.fullName = fullName;
String[] l = fullName.split(" ");
givenName = l[0];
familyName = l[1];
}
public void setFamilyName(String s) {
public void setFamilyName(String familyName) {
this.familyName = familyName;
fullName = givenName + " " + familyName;
}
public void setGivenName(String s) {
public void setGivenName(String givenName) {
this.givenName = givenName;
fullName = givenName + " " + familyName;
}
public String getFullName() {
return "";
return fullName;
}
public String getFamilyName() {
return "";
return familyName;
}
public String getGivenName() {
return "";
return givenName;
}
}

View File

@@ -0,0 +1,79 @@
package oving6.highscorelist;
import java.util.List;
import java.util.ArrayList;
import java.util.Collections;
public class HighscoreList {
private int maxSize = 0;
private List<Integer> results = new ArrayList<Integer>();
private ArrayList<HighscoreListListener> highscoreListListeners = new ArrayList<HighscoreListListener>();
HighscoreList(int maxSize) {
this.maxSize = maxSize;
}
public int size() {
return results.size();
}
public int getElement(int i) {
if (i < 0 || i >= results.size()) {
throw new IllegalArgumentException("Invalid value for i");
}
return results.get(i);
}
public void addResult(int r) {
if (maxSize <= 0) {
return;
}
int idx = results.size();
for (int i = 0; i < results.size(); i++) {
if (r < results.get(i)) {
idx = i;
break;
}
}
if (results.size() < maxSize || idx < results.size()) {
results.add(r);
Collections.sort(results);
while (results.size() > maxSize) {
results.removeLast();
}
sendHighscoreEvent(idx);
}
}
public void addHighscoreListListener(HighscoreListListener listener) {
highscoreListListeners.add(listener);
}
public void removeHighscoreListListener(HighscoreListListener listener) {
highscoreListListeners.remove(listener);
}
private void sendHighscoreEvent(int i) {
for (HighscoreListListener listener : highscoreListListeners) {
listener.listChanged(this, i);
}
}
@Override
public String toString() {
String s = "[";
for (int i = 0; i < results.size(); i++) {
s += results.get(i);
if (i < results.size() - 1) {
s += ",";
}
}
s += "]";
return s;
}
}

View File

@@ -0,0 +1,5 @@
package oving6.highscorelist;
public interface HighscoreListListener {
void listChanged(HighscoreList l, int i);
}

View File

@@ -0,0 +1,35 @@
package oving6.highscorelist;
import java.util.Scanner;
public class HighscoreProgram implements HighscoreListListener {
HighscoreList highscoreList;
void main() {
init();
run();
}
void init() {
highscoreList = new HighscoreList(10);
highscoreList.addHighscoreListListener(this);
}
void run() {
System.out.println("Starting listener:");
Scanner scanner = new Scanner(System.in);
while (true) {
if (scanner.hasNextInt()) {
highscoreList.addResult(scanner.nextInt());
} else {
scanner.next();
}
}
}
@Override
public void listChanged(HighscoreList l, int i) {
System.out.println("List[" + i + "]: " + l.toString());
}
}

View File

@@ -0,0 +1,48 @@
package oving6.logger;
public class DistributingLogger implements ILogger {
ILogger errorLogger;
ILogger warningLogger;
ILogger infoLogger;
DistributingLogger(ILogger errorLogger, ILogger warningLogger, ILogger infoLogger) {
this.errorLogger = errorLogger;
this.warningLogger = warningLogger;
this.infoLogger = infoLogger;
}
void setLogger(String severity, ILogger logger) {
switch (severity) {
case "error":
errorLogger = logger;
break;
case "warning":
warningLogger = logger;
break;
case "info":
infoLogger = logger;
break;
default:
throw new IllegalArgumentException("Invalid severity level for logger");
}
}
@Override
public void log(String severity, String message, Exception exception) {
switch (severity) {
case "error":
errorLogger.log(severity, message, exception);
break;
case "warning":
warningLogger.log(severity, message, exception);
break;
case "info":
infoLogger.log(severity, message, exception);
break;
default:
throw new IllegalArgumentException("Invalid severity level for logger");
}
}
}

View File

@@ -0,0 +1,29 @@
package oving6.logger;
import java.util.HashMap;
public class FilteringLogger {
private ILogger logger;
private HashMap<String, Boolean> logging = new HashMap<String, Boolean>();
FilteringLogger(ILogger logger, String... severities) {
this.logger = logger;
for (String severity : severities) {
logging.put(severity, true);
}
}
boolean isLogging(String severity) {
return logging.getOrDefault(severity, false);
}
void setIsLogging(String severity, boolean value) {
logging.put(severity, value);
}
void log(String severity, String message, Exception exception) {
if (isLogging(severity)) {
logger.log(severity, message, exception);
}
}
}

View File

@@ -0,0 +1,9 @@
package oving6.logger;
public interface ILogger {
String ERROR = "error";
String WARNING = "warning";
String INFO = "info";
void log(String severity, String message, Exception exception);
}

View File

@@ -0,0 +1,31 @@
package oving6.logger;
import java.io.OutputStream;
public class StreamLogger implements ILogger {
private OutputStream stream;
private String formatString = "%s: %s (%s)";
StreamLogger(OutputStream stream) {
this.stream = stream;
}
@Override
public void log(String severity, String message, Exception exception) {
try {
String logMessage = String.format(formatString, severity, message, exception) + "\n";
stream.write(logMessage.getBytes());
stream.flush();
} catch (Exception e) {
System.out.println(e);
}
}
void setFormatString(String formatString) {
if (formatString == null) {
throw new IllegalArgumentException("formatString cannot be null");
}
this.formatString = formatString;
}
}

View File

@@ -0,0 +1,77 @@
package oving7.abstractaccount;
/**
* A bank consists of many different types of accounts: credit accounts, debit
* accounts, savings
* accounts, etc. Since these have a lot in common, e.g. all have a balance, it
* is practical to
* collect as much of the common logic as possible in a superclass, which all
* can inherit from.
* However, this superclass is not a type of account in itself, and therefore we
* make it
* {@code abstract}, so that it cannot be instantiated. The concrete account
* classes that inherit
* from it, must of course be instantiable. The methods defined in the
* {@code AbstractAccount} class
* is similar to that of the Account interface in the SavingsAccount task.
*/
public abstract class AbstractAccount {
// AbstractAccount has a state {@code balance} for the account balance. The
// balance should
// either be set to 0.0 by default or in the constructor
// TODO: Add fields and potentially a constructor here
protected double balance = 0.0;
/**
* Decreases the account balance by the specified amount. Note that the rules
* for withdrawals
* are different for the classes that implement {@code AbstractAccount}, and
* must therefore be
* implemented in each class.
*
* @param amount the amount to withdraw
* @throws IllegalArgumentException if the amount cannot be withdrawn
*/
// TODO: Define abstract method {@code void internalWithdraw} here
protected abstract void internalWithdraw(double amount);
/**
* Increases the account balance by the specified amount.
*
* @param amount the amount to deposit
* @throws IllegalArgumentException if the amount is not positive
*/
public void deposit(double amount) {
if (amount < 0.0) {
throw new IllegalArgumentException("cannot deposit amount less than zero");
}
balance += amount;
}
/**
* This method calls the {@link #internalWithdraw()} method, which is
* implemented in each
* subclass.
*
* @param amount the amount to withdraw
* @throws IllegalArgumentException if the amount is not positive
*/
public void withdraw(double amount) {
// TODO: Implement this method
if (amount < 0.0) {
throw new IllegalArgumentException("withdrawal amount cannot be less than zero");
}
internalWithdraw(amount);
}
/**
* @return the current balance of the account
*/
public double getBalance() {
return balance;
}
}

View File

@@ -0,0 +1,65 @@
package oving7.abstractaccount;
/**
* A {@code CreditAccount} has in addition to {@code balance} a state for
* {@code creditLine}, i.e.
* available credit on the account. This credit line allows the account to be
* overdrawn (that the
* balance is negative) within the credit line. If {@link #internalWithdraw()}
* tries to withdraw
* more money than is available, taking the credit line into account, an
* {@code IllegalArgumentException} should be thrown.
*
* @see AbstractAccount
*/
public class CreditAccount extends AbstractAccount {
private double creditLine;
/**
* Initializes a new {@code CreditAccount} with the specified credit line.
*
* @param creditLine the credit line
* @throws IllegalArgumentException if the credit line is negative
*/
public CreditAccount(double creditLine) {
this.creditLine = creditLine;
}
protected void internalWithdraw(double amount) {
if (amount > (balance + creditLine)) {
throw new IllegalArgumentException("cannot withdraw more than balance plus creditLine");
}
balance -= amount;
}
/**
* @return the credit line
*
* @see CreditAccountTest#testCreditLine()
*/
public double getCreditLine() {
return creditLine;
}
/**
* Sets the credit line.
*
* @param creditLine the credit line
* @throws IllegalArgumentException if the credit line is negative
* @throws IllegalStateException if the new credit line does not cover the
* existing balance
*
* @see CreditAccountTest#testCreditLine()
*/
public void setCreditLine(double creditLine) {
if (creditLine < 0.0) {
throw new IllegalArgumentException("credit line cannot be less than zero");
}
if (creditLine + balance < 0.0) {
throw new IllegalStateException("credit line plus balance must be greater than or equal to zero");
}
this.creditLine = creditLine;
}
}

View File

@@ -0,0 +1,25 @@
package oving7.abstractaccount;
/**
* A debit account is the simplest form of account, where the only requirement
* is that the balance
* at any time must be greater than or equal to {@code 0.0}.
* {@code DebitAccount} extends (inherits
* from) {@link AbstractAccount} and ensure that the balance never falls below
* {@code 0.0}. If an
* attempt is made to withdraw more money than is available, an
* {@code IllegalArgumentException}
* should be thrown.
*
* @see AbstractAccount
*/
public class DebitAccount extends AbstractAccount {
@Override
protected void internalWithdraw(double amount) {
if (balance < amount) {
throw new IllegalArgumentException("cannot withdraw amount greater than balance for debit account");
}
balance -= amount;
}
}

View File

@@ -0,0 +1,49 @@
package oving7.abstractaccount;
/**
* A {@code SavingsAccount} can only have a positive balance. In addition, the
* account has
* withdrawal restrictions. A {@code SavingsAccount} has {@code x} number of
* {@code withdrawals}. If
* you want to withdraw money after all withdrawals have been used up, the
* balance should be charged
* a {@code fee}. If the balance is too low to cover the fee, an
* {@code IllegalArgumentException}
* should be thrown.
*
* @see AbstractAccount
*/
public class SavingsAccount extends AbstractAccount {
private int withdrawals;
private double fee;
/**
* Initializes a new {@code SavingsAccount} with the specified number of
* withdrawals and fee.
*
* @param withdrawals the number of withdrawals
* @param fee the fee
* @throws IllegalArgumentException if the number of withdrawals or the fee is
* negative
*/
public SavingsAccount(int withdrawals, double fee) {
if (withdrawals < 0 || fee < 0.0) {
throw new IllegalArgumentException("cannot have less than zero withdrawals or fee of less than zero");
}
this.withdrawals = withdrawals;
this.fee = fee;
}
protected void internalWithdraw(double amount) {
withdrawals -= 1;
double s = amount;
if (withdrawals < 0) {
s += fee;
}
if (balance < s) {
throw new IllegalArgumentException("balance not great enough to cover fees");
}
balance -= s;
}
}

View File

View File

@@ -0,0 +1,9 @@
package oving7.savingsaccount;
public interface Account {
void deposit(double amount);
void withdraw(double amount);
double getBalance();
}

View File

@@ -0,0 +1,41 @@
package oving7.savingsaccount;
public class BSU extends SavingsAccount {
private double depositedThisYear = 0.0;
private double depositMax;
BSU(double interestRate, double depositMax) {
super(interestRate);
this.depositMax = depositMax;
}
public double getTaxDeduction() {
return depositedThisYear * 0.2;
}
@Override
public void deposit(double amount) {
if ((depositedThisYear + amount) > depositMax) {
throw new IllegalStateException(
"cannot deposit more than the maximum deposit, which has a value of " + depositMax);
}
super.deposit(amount);
depositedThisYear += amount;
}
@Override
public void withdraw(double amount) {
if (amount > depositedThisYear) {
throw new IllegalStateException(
"cannot withdraw more than has been deposited this year, which has a value of "
+ depositedThisYear);
}
super.withdraw(amount);
}
@Override
public void endYearUpdate() {
super.endYearUpdate();
depositedThisYear = 0.0;
}
}

View File

@@ -0,0 +1,31 @@
package oving7.savingsaccount;
public class ForeldreSpar extends SavingsAccount {
private int withdrawalsMax;
private int remainingWithdrawals;
ForeldreSpar(double interestRate, int withdrawalsMax) {
super(interestRate);
this.withdrawalsMax = withdrawalsMax;
remainingWithdrawals = withdrawalsMax;
}
int getRemainingWithdrawals() {
return remainingWithdrawals;
}
@Override
public void withdraw(double amount) {
if (remainingWithdrawals <= 0) {
throw new IllegalStateException("Cannot withdraw more than " + withdrawalsMax + " times a year");
}
super.withdraw(amount);
remainingWithdrawals -= 1;
}
@Override
public void endYearUpdate() {
super.endYearUpdate();
remainingWithdrawals = withdrawalsMax;
}
}

View File

@@ -0,0 +1,35 @@
package oving7.savingsaccount;
public class SavingsAccount implements Account {
private double balance = 0.0;
private double interestRate;
SavingsAccount(double interestRate) {
this.interestRate = interestRate;
}
public void deposit(double amount) {
if (amount <= 0) {
throw new IllegalArgumentException("deposit must be greater than zero");
}
balance += amount;
}
public void withdraw(double amount) {
if (amount <= 0) {
throw new IllegalArgumentException("withdraw must be greater than zero");
}
if (amount > balance) {
throw new IllegalStateException("cannot withdraw more than current balance");
}
balance -= amount;
}
public double getBalance() {
return balance;
}
public void endYearUpdate() {
balance += balance * interestRate;
}
}

View File

@@ -0,0 +1,63 @@
package oving7.train;
/**
* One of two different types of train cars, both specialized versions for different purposes. A
* {@code CargoCar} represents a cargo car that transports various things and stuff.
*
* @see TrainCar
* @see PassengerCar
*/
public class CargoCar extends TrainCar {
private int cargoWeight;
/**
* Constructor for the cargo car.
*
* @param deadWeight the weight of an empty cargo car
* @param cargoWeight the weight of the cargo in the cargo car
* @throws IllegalArgumentException if either deadWeight or cargoWeight is negative
*
* @see CargoCarTest#testWeight()
*/
public CargoCar(int deadWeight, int cargoWeight) {
super(deadWeight);
this.cargoWeight = cargoWeight;
}
/**
* @return the weight of the cargo in the cargo car
*
* @see CargoCarTest#testWeight()
*/
public int getCargoWeight() {
return cargoWeight;
}
/**
* @param cargoWeight the weight of the cargo in the cargo car
* @throws IllegalArgumentException if cargoWeight is negative
*
* @see CargoCarTest#testWeight()
*/
public void setCargoWeight(int cargoWeight) {
if (cargoWeight < 0) {
throw new IllegalArgumentException("cargo weight cannot be less than zero");
}
this.cargoWeight = cargoWeight;
}
@Override
public int getTotalWeight() {
return cargoWeight + super.getDeadWeight();
}
@Override
public String toString() {
return "CargoCar";
}
public static void main(String[] args) {
}
}

View File

@@ -0,0 +1,65 @@
package oving7.train;
/**
* One of two different types of train cars, both specialized versions for different purposes. A
* {@code PassengerCar} represents a passenger car that transports passengers.
*
* @see TrainCar
* @see CargoCar
*/
public class PassengerCar extends TrainCar {
private int passengerCount;
/**
* Constructor for the passenger car.
*
* @param deadWeight the weight of an empty passenger car
* @param passengerCount the number of passengers in the passenger car
* @throws IllegalArgumentException if either deadWeight or passengerCount is negative
*
* @see PassengerCarTest#testWeight()
*/
public PassengerCar(int deadWeight, int passengerCount) {
this.passengerCount = passengerCount;
super(deadWeight);
}
/**
* @return the number of passengers in the passenger car
*
* @see PassengerCarTest#testWeight()
*/
public int getPassengerCount() {
return passengerCount;
}
/**
* @param passengerCount the number of passengers in the passenger car
* @throws IllegalArgumentException if passengerCount is negative
*
* @see PassengerCarTest#testWeight()
*/
public void setPassengerCount(int passengerCount) {
if (passengerCount < 0) {
throw new IllegalArgumentException("passenger count cannot be less than zero");
}
this.passengerCount = passengerCount;
}
@Override
public int getTotalWeight() {
// To calculate the total weight of the passenger car, you can assume that an average
// passenger weighs 80 kg
return super.getDeadWeight() + passengerCount * 80;
}
@Override
public String toString() {
return "PassengerCar";
}
public static void main(String[] args) {
}
}

View File

@@ -0,0 +1,96 @@
package oving7.train;
import java.util.ArrayList;
import java.util.stream.Stream;
import java.util.stream.Collectors;
/**
* The class {@code Train} represents a train that consists of one or more train cars.
*
* @see TrainCar
* @see CargoCar
* @see PassengerCar
*/
public class Train {
private ArrayList<TrainCar> trainCars = new ArrayList<TrainCar>();
/**
* @param trainCar the train car to check for
* @return {@code true} if the train contains the train car, {@code false} otherwise
*
* @see TrainTest#testAddCarToTrain()
*/
public boolean contains(TrainCar trainCar) {
return trainCars.contains(trainCar);
}
/**
* Adds a train car to the train.
*
* @param trainCar the train car to add
* @throws IllegalArgumentException if the train car is {@code null}
*
* @see TrainTest#testAddCarToTrain()
*/
public void addTrainCar(TrainCar trainCar) {
if (trainCar == null) {
throw new IllegalArgumentException("train car cannot be null");
}
trainCars.add(trainCar);
}
/**
* @return the sum of the total weight of all the train cars in the train. There is no need to
* take the weight of the locomotive into account
*
* @see TrainTest#testTotalTrainWeight()
*/
public int getTotalWeight() {
return trainCars.stream()
.mapToInt(car -> car.getTotalWeight())
.sum();
}
/**
* @return similar to {@link PassengerCar#getPassengerCount()}, but for the entire train
*
* @see TrainTest#testPassengerCount()
*/
public int getPassengerCount() {
return trainCars.stream()
.filter(car -> car instanceof PassengerCar)
.map(car -> (PassengerCar) car)
.mapToInt(car -> car.getPassengerCount())
.sum();
}
/**
* @return similar to {@link CargoCar#getCargoWeight()}, but for the entire train
*
* @see TrainTest#testCargoWeight()
*/
public int getCargoWeight() {
return trainCars.stream()
.filter(car -> car instanceof CargoCar)
.map(car -> (CargoCar) car)
.mapToInt(car -> car.getCargoWeight())
.sum();
}
/**
* @return a string representation of the train. The string should consist of the
* {@link #toString()}s of all train cars in the train
*/
@Override
public String toString() {
return trainCars.stream()
.map(car -> car.toString())
.collect(Collectors.joining(", "));
}
// TODO: Write a main method to test the class
public static void main(String[] args) {
}
}

View File

@@ -0,0 +1,74 @@
package oving7.train;
/**
* The class {@code TrainCar} represents a simple and general train car.
*/
public class TrainCar {
private int deadWeight;
/**
* Constructor for a train car.
*
* @param deadWeight the weight of an empty train car
* @throws IllegalArgumentException if deadWeight is negative
*
* @see TrainCarTest#testDeadWeight()
*/
public TrainCar(int deadWeight) {
if (deadWeight < 0) {
throw new IllegalArgumentException("dead weight cannot be less than zero");
}
this.deadWeight = deadWeight;
}
/**
* @param deadWeight the weight of an empty train car. In other words, the weight of only the
* carriage, without passengers and cargo
* @throws IllegalArgumentException if deadWeight is negative
*
* @see TrainCarTest#testDeadWeight()
*/
public void setDeadWeight(int deadWeight) {
if (deadWeight < 0) {
throw new IllegalArgumentException("dead weight cannot be less than zero");
}
this.deadWeight = deadWeight;
}
/**
* @return the weight of an empty train car. In other words, the weight of only the carriage,
* without passengers and cargo
*
* @see TrainCarTest#testDeadWeight()
*/
public int getDeadWeight() {
return deadWeight;
}
/**
* @return the total weight of the train car. Note that this method should also be callable on
* subclasses and still return the total weight of the train car (keyword:
* redefinition).
*
* @see TrainCarTest#testDeadWeight()
*/
public int getTotalWeight() {
return deadWeight;
}
/**
* @return a string representation of the train car. The string should contain the type of the
* train car and the total weight of the train car. For {@link PassengerCar}, the number
* of passengers should also be included. For {@link CargoCar}, the weight of the cargo
* should also be included.
*/
@Override
public String toString() {
return "TrainCar";
}
public static void main(String[] args) {
}
}

View File

@@ -0,0 +1,79 @@
package oving7.abstractaccount;
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 static final double epsilon = 0.0005;
private CreditAccount sub;
@BeforeEach
public void setUp() {
sub = new CreditAccount(10_000.0);
}
@Test
@DisplayName("Check that deposits work as expected")
public void testDeposit() {
assertEquals(0.0, sub.getBalance(), epsilon, "The account balance was incorrect");
sub.deposit(10_000.0);
assertEquals(10_000.0, sub.getBalance(), epsilon, "The account balance was incorrect");
assertThrows(IllegalArgumentException.class, () -> {
sub.deposit(-10_000.0);
}, "Negative deposit should have triggered an IllegalArgumentException!");
assertEquals(10_000.0, sub.getBalance(), epsilon, "The account balance was incorrect");
}
@Test
@DisplayName("Check that withdrawals work as expected")
public void testWithdraw() {
sub.deposit(20_000.0);
sub.withdraw(5000.0);
assertEquals(15_000.0, sub.getBalance(), epsilon, "The account balance was incorrect");
assertThrows(IllegalArgumentException.class, () -> {
sub.withdraw(-10_000.0);
}, "Negative withdrawal should have triggered an IllegalArgumentException!");
assertEquals(15_000.0, sub.getBalance(), epsilon, "The account balance was incorrect");
sub.withdraw(20_000.0);
assertEquals(-5000.0, sub.getBalance(), epsilon, "The account balance was incorrect");
assertThrows(IllegalArgumentException.class, () -> {
sub.withdraw(20_000.0);
}, "Withdrawal exceeding the credit limit should have triggered an IllegalArgumentException");
}
@Test
@DisplayName("Check that the credit limit works as expected")
public void testCreditLine() {
assertEquals(10_000.0, sub.getCreditLine(), epsilon, "The credit limit was incorrect");
sub.setCreditLine(5000.0);
assertEquals(5000.0, sub.getCreditLine(), epsilon, "The credit limit was incorrect");
assertThrows(IllegalArgumentException.class, () -> {
sub.setCreditLine(-5000.0);
}, "Cannot have a negative credit limit");
assertEquals(5000.0, sub.getCreditLine(), epsilon, "The credit limit was incorrect");
sub.withdraw(4000.0);
assertThrows(IllegalStateException.class, () -> {
sub.setCreditLine(3000.0);
}, "Cannot set a credit limit that would result in an invalid balance");
assertEquals(-4000.0, sub.getBalance(), epsilon, "The balance was incorrect");
assertEquals(5000.0, sub.getCreditLine(), epsilon, "The credit limit was incorrect");
}
}

View File

@@ -0,0 +1,52 @@
package oving7.abstractaccount;
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 static final double epsilon = 0.0005;
private DebitAccount sub;
@BeforeEach
public void setUp() {
sub = new DebitAccount();
}
@Test
@DisplayName("Check that deposits work as expected")
public void testDeposit() {
assertEquals(0.0, sub.getBalance(), epsilon, "The account balance was incorrect");
sub.deposit(10_000.0);
assertEquals(10_000.0, sub.getBalance(), epsilon, "The account balance was incorrect");
assertThrows(IllegalArgumentException.class, () -> {
sub.deposit(-10_000.0);
}, "Negative deposit should have triggered an IllegalArgumentException!");
assertEquals(10_000.0, sub.getBalance(), epsilon, "The account balance was incorrect");
}
@Test
@DisplayName("Check that withdrawals work as expected")
public void testWithdraw() {
sub.deposit(20_000.0);
sub.withdraw(5000.0);
assertEquals(15_000.0, sub.getBalance(), epsilon, "The account balance was incorrect");
assertThrows(IllegalArgumentException.class, () -> {
sub.withdraw(-10_000.0);
}, "Negative withdrawal should have triggered an IllegalArgumentException!");
assertEquals(15_000.0, sub.getBalance(), epsilon, "The account balance was incorrect");
assertThrows(IllegalArgumentException.class, () -> {
sub.withdraw(20_000.0);
}, "Withdrawal exceeding the balance should have triggered an IllegalArgumentException");
}
}

View File

@@ -0,0 +1,65 @@
package oving7.abstractaccount;
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 SavingsAccountTest {
private static final double epsilon = 0.0005;
private SavingsAccount sub;
@BeforeEach
public void setUp() {
sub = new SavingsAccount(1, 50.0);
}
@Test
@DisplayName("Check that deposits work as expected")
public void testDeposit() {
assertEquals(0.0, sub.getBalance(), epsilon, "The account balance was incorrect");
sub.deposit(10_000.0);
assertEquals(10_000.0, sub.getBalance(), epsilon, "The account balance was incorrect");
assertThrows(IllegalArgumentException.class, () -> {
sub.deposit(-10_000.0);
}, "Negative deposit should have triggered an IllegalArgumentException!");
assertEquals(10_000.0, sub.getBalance(), epsilon, "The account balance was incorrect");
}
@Test
@DisplayName("Check that withdrawals work as expected")
public void testWithdraw() {
sub.deposit(20_000.0);
sub.withdraw(5000.0);
assertEquals(15_000.0, sub.getBalance(), epsilon, "The account balance was incorrect");
assertThrows(IllegalArgumentException.class, () -> {
sub.withdraw(-10_000.0);
}, "Negative withdrawal should have triggered an IllegalArgumentException!");
assertEquals(15_000.0, sub.getBalance(), epsilon, "The account balance was incorrect");
assertThrows(IllegalArgumentException.class, () -> {
sub.withdraw(20_000.0);
}, "Withdrawal exceeding the balance should have triggered an IllegalArgumentException");
assertEquals(15_000.0, sub.getBalance(), epsilon, "The account balance was incorrect");
sub.withdraw(10_000.0);
assertEquals(4950.0, sub.getBalance(), epsilon,
"The account balance was incorrect after the fee was deducted");
assertThrows(IllegalArgumentException.class, () -> {
sub.withdraw(4930.0);
}, "Withdrawal exceeding the balance + fee should have triggered an IllegalArgumentException");
assertEquals(4950.0, sub.getBalance(), epsilon,
"The account balance was incorrect after the fee was deducted");
}
}

View File

@@ -0,0 +1,87 @@
package oving7.card;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
public class CardDeckTest {
private Card s1;
private Card s2;
private Card h1;
private Card h2;
private Card d1;
private Card d2;
private Card c1;
private Card c2;
private CardDeck deck;
private List<Card> expected;
private static void testCards(CardContainer it, List<Card> expected) {
assertEquals(expected.size(), it.getCardCount());
for (int i = 0; i < expected.size(); i++) {
Card expectedCard = expected.get(i);
Card actualCard = it.getCard(i);
assertEquals(expectedCard.getSuit(), actualCard.getSuit(),
String.format("Card number %d should have been %s, but was %s", i + 1,
expectedCard, actualCard));
assertEquals(expectedCard.getFace(), actualCard.getFace(),
String.format("Card number %d should have been %s, but was %s", i + 1,
expectedCard, actualCard));
i++;
}
}
private static void testCards(Iterator<Card> actual, Iterator<Card> expected) {
while (expected.hasNext()) {
assertTrue(actual.hasNext());
Card expectedCard = expected.next();
Card actualCard = actual.next();
assertEquals(expectedCard.getSuit(), actualCard.getSuit(), String
.format("The card should have been %s, but was %s", expectedCard, actualCard));
assertEquals(expectedCard.getFace(), actualCard.getFace(), String
.format("The card should have been %s, but was %s", expectedCard, actualCard));
}
}
@BeforeEach
public void setUp() {
deck = new CardDeck(2);
s1 = new Card('S', 1);
s2 = new Card('S', 2);
h1 = new Card('H', 1);
h2 = new Card('H', 2);
d1 = new Card('D', 1);
d2 = new Card('D', 2);
c1 = new Card('C', 1);
c2 = new Card('C', 2);
expected = new LinkedList<>(List.of(s1, s2, h1, h2, d1, d2, c1, c2));
}
@Test
@DisplayName("Test maxCardCount")
public void testMaxCardCount() {
assertTrue(deck instanceof CardContainerImpl);
assertEquals(52, deck.getMaxCardCount());
}
@Test
@DisplayName("Test that CardDeckImpl implements CardContainer")
public void testCardContainer() {
CardDeckTest.testCards(deck, expected);
}
@Test
@DisplayName("Test that CardDeckImpl implements Iterable")
public void testDeckIterator() {
CardDeckTest.testCards(deck.iterator(), expected.iterator());
}
}

View File

@@ -0,0 +1,82 @@
package oving7.card;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.Iterator;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
public class CardHandTest {
private Card c2;
private Card s1;
private CardHand hand;
private List<Card> expected;
private static void testCards(CardContainer it, List<Card> expected) {
assertEquals(expected.size(), it.getCardCount());
for (int i = 0; i < expected.size(); i++) {
Card expectedCard = expected.get(i);
Card actualCard = it.getCard(i);
assertEquals(expectedCard.getSuit(), actualCard.getSuit(),
String.format("Card number %d should have been %s, but was %s", i + 1,
expectedCard, actualCard));
assertEquals(expectedCard.getFace(), actualCard.getFace(),
String.format("Card number %d should have been %s, but was %s", i + 1,
expectedCard, actualCard));
i++;
}
}
private static void testCards(Iterator<Card> actual, Iterator<Card> expected) {
while (expected.hasNext()) {
assertTrue(actual.hasNext());
Card expectedCard = expected.next();
Card actualCard = actual.next();
assertEquals(expectedCard.getSuit(), actualCard.getSuit(), String
.format("The card should have been %s, but was %s", expectedCard, actualCard));
assertEquals(expectedCard.getFace(), actualCard.getFace(), String
.format("The card should have been %s, but was %s", expectedCard, actualCard));
}
}
@BeforeEach
public void setUp() {
hand = new CardHand(2);
s1 = new Card('S', 1);
c2 = new Card('C', 2);
expected = List.of(s1, c2);
}
@Test
@DisplayName("Test cardCount")
public void testCardCount() {
assertTrue(hand instanceof CardContainerImpl);
assertEquals(0, hand.getCardCount(), "CardCount should have been 0 at the start");
hand.addCard(new Card('S', 1));
hand.addCard(new Card('S', 2));
assertEquals(2, hand.getCardCount(), "CardCount should have been 2 at the start");
}
@Test
@DisplayName("Test that cardDeckImpl implements cardContainer")
public void testCardContainer() {
hand.addCard(new Card('S', 1));
hand.addCard(new Card('C', 2));
CardHandTest.testCards(hand, expected);
}
@Test
@DisplayName("Test that cardDeckImpl implements iterable")
public void testDeckIterator() {
hand.addCard(new Card('S', 1));
hand.addCard(new Card('C', 2));
CardHandTest.testCards(hand.iterator(), expected.iterator());
}
}

View File

@@ -0,0 +1,162 @@
package oving7.observablelist;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
public class ObservableHighscoreListTest {
private int pos1;
private int pos2;
private ObservableHighscoreList highscoreList;
private static void checkHighscoreList(String contextMessage, ObservableHighscoreList list,
List<Integer> elements) {
assertEquals(elements.size(), list.size(),
contextMessage + " -> Testing the length of the highscore list");
int i = 0;
for (int element : elements) {
assertEquals(element, list.getElement(i),
contextMessage + " -> Testing that the element at position " + i + " matches");
i++;
}
}
private void addResultWithListener(int pos, int element) {
pos1 = pos;
highscoreList.addResult(element);
// Check that the position that was changed is the same as the one sent to the listener
assertEquals(pos1, pos2, "Added " + element + " at position " + pos
+ " -> Testing the position received by the listener");
}
@BeforeEach
public void setUp() {
highscoreList = new ObservableHighscoreList(3);
pos1 = -1;
pos2 = -1;
}
@Test
@DisplayName("Test constructor")
public void testConstructor() {
assertEquals(0, highscoreList.size(), "Testing initialization of the highscore list");
}
@Test
@DisplayName("Add results (simple)")
public void testAddElementSimple() {
highscoreList.addResult(5);
ObservableHighscoreListTest.checkHighscoreList("Added 5 to an empty list", highscoreList,
List.of(5));
highscoreList.addResult(6);
ObservableHighscoreListTest.checkHighscoreList("Added 6 to the list [5]", highscoreList,
List.of(5, 6));
highscoreList.addResult(2);
ObservableHighscoreListTest.checkHighscoreList("Added 2 to the list [5, 6]", highscoreList,
List.of(2, 5, 6));
}
@Test
@DisplayName("Add results - list becomes too long")
public void testAddElementMoreThanMax() {
highscoreList.addResult(5);
highscoreList.addResult(6);
highscoreList.addResult(2);
ObservableHighscoreListTest.checkHighscoreList("Added 5, 6, and 2 to the list",
highscoreList, List.of(2, 5, 6));
highscoreList.addResult(3);
ObservableHighscoreListTest.checkHighscoreList("Added 3 to the list [2, 5, 6]",
highscoreList, List.of(2, 3, 5));
highscoreList.addResult(7);
ObservableHighscoreListTest.checkHighscoreList("Added 7 to the list [2, 3, 5]",
highscoreList, List.of(2, 3, 5));
}
@Test
@DisplayName("Add two identical elements")
public void testAddElementDuplicate() {
highscoreList.addResult(5);
highscoreList.addResult(6);
highscoreList.addResult(2);
ObservableHighscoreListTest.checkHighscoreList("Added 5, 6, and 2 to the list",
highscoreList, List.of(2, 5, 6));
highscoreList.addResult(2);
ObservableHighscoreListTest.checkHighscoreList("Added 2 to the list [2, 5, 6]",
highscoreList, List.of(2, 2, 5));
}
@Test
@DisplayName("Test listeners (simple)")
public void testListListenersSimple() {
// Mock a listener
ObservableListListener listener = (list, pos) -> pos2 = pos;
highscoreList.addObservableListListener(listener);
this.addResultWithListener(0, 5);
ObservableHighscoreListTest.checkHighscoreList("Added 5 to the list []", highscoreList,
List.of(5));
this.addResultWithListener(1, 6);
ObservableHighscoreListTest.checkHighscoreList("Added 6 to the list [5]", highscoreList,
List.of(5, 6));
this.addResultWithListener(0, 2);
ObservableHighscoreListTest.checkHighscoreList("Added 2 to the list [5, 6]", highscoreList,
List.of(2, 5, 6));
}
@Test
@DisplayName("With listener - list becomes too long")
public void testListListenerMoreThanMax() {
// Mock a listener
ObservableListListener listener = (list, pos) -> pos2 = pos;
highscoreList.addObservableListListener(listener);
highscoreList.addResult(5);
highscoreList.addResult(6);
highscoreList.addResult(2);
ObservableHighscoreListTest.checkHighscoreList("Added 5, 6, and 2 to the list",
highscoreList, List.of(2, 5, 6));
this.addResultWithListener(1, 3);
ObservableHighscoreListTest.checkHighscoreList("Added 3 to the list [2, 5, 6]",
highscoreList, List.of(2, 3, 5));
// Reset pos2 since the next element falls outside the list and is therefore not updated by
// itself and sent to the listener
pos2 = -1;
this.addResultWithListener(-1, 7);
ObservableHighscoreListTest.checkHighscoreList("Added 7 to the list [2, 3, 5]",
highscoreList, List.of(2, 3, 5));
}
@Test
@DisplayName("With listener - two identical elements")
public void testListListenerDuplicate() {
// Mock a listener
ObservableListListener listener = (list, pos) -> pos2 = pos;
highscoreList.addObservableListListener(listener);
highscoreList.addResult(5);
highscoreList.addResult(6);
highscoreList.addResult(2);
ObservableHighscoreListTest.checkHighscoreList("Added 5, 6, and 2 to the list",
highscoreList, List.of(2, 5, 6));
this.addResultWithListener(1, 2);
ObservableHighscoreListTest.checkHighscoreList("Added 2 to the list [2, 5, 6]",
highscoreList, List.of(2, 2, 5));
}
}

View File

@@ -0,0 +1,106 @@
package oving7.observablelist;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
public class ObservableListTest {
private int pos1;
private int pos2;
private ObservableList observableList;
private static void checkObservableList(ObservableList list, List<Integer> elements,
String contextMessage) {
assertEquals(elements.size(), list.size(),
contextMessage + " -> Testing the length of observableList");
int i = 0;
for (int element : elements) {
assertEquals(element, list.getElement(i),
contextMessage + " -> Testing that the element at position " + i + " matches");
i++;
}
}
private void addElementWithListener(int pos, int element) {
pos1 = pos;
observableList.addElement(pos, element);
// Check that the position that was changed is the same as the one sent to the listener
assertEquals(pos1, pos2, "Added " + element + " at position " + pos
+ " -> Testing the position received by the listener");
}
@BeforeEach
public void setUp() {
observableList = new ObservableList() {
@Override
public boolean acceptsElement(Object element) {
return element instanceof Integer;
}
};
pos1 = -1;
pos2 = -1;
}
@Test
@DisplayName("Test constructor")
public void testConstructor() {
assertEquals(0, observableList.size());
}
@Test
@DisplayName("Test acceptance of elements")
public void testAcceptsElement() {
assertTrue(observableList.acceptsElement(5), "Testing that the list accepts integers");
assertFalse(observableList.acceptsElement("5"),
"Testing that the list does not accept strings");
assertThrows(IllegalArgumentException.class, () -> {
observableList.addElement("5");
}, "Testing that the list cannot accept elements of type string");
}
@Test
@DisplayName("Test adding elements")
public void testAddElement() {
observableList.addElement(5);
ObservableListTest.checkObservableList(observableList, List.of(5),
"Added 5 to an empty list");
observableList.addElement(6);
ObservableListTest.checkObservableList(observableList, List.of(5, 6),
"Added 6 to the list [5]");
observableList.addElement(0, 2);
ObservableListTest.checkObservableList(observableList, List.of(2, 5, 6),
"Added 2 at position 0 in the list [5, 6]");
}
@Test
@DisplayName("Test listener")
public void testListListener() {
ObservableListListener listener = (list, pos) -> pos2 = pos;
observableList.addObservableListListener(listener);
this.addElementWithListener(0, 5);
ObservableListTest.checkObservableList(observableList, List.of(5),
"Added 5 to the list []");
this.addElementWithListener(1, 6);
ObservableListTest.checkObservableList(observableList, List.of(5, 6),
"Added 6 to the list [5]");
this.addElementWithListener(0, 2);
ObservableListTest.checkObservableList(observableList, List.of(2, 5, 6),
"Added 2 to the list [5, 6]");
}
}

View File

@@ -0,0 +1,84 @@
package oving7.savingsaccount;
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 BSUTest {
private static final double epsilon = 0.001;
private BSU bsu;
@BeforeEach
public void setUp() {
bsu = new BSU(0.05, 25_000.0);
}
@Test
@DisplayName("Check that deposits work as expected")
public void testDeposit() {
bsu.deposit(10_000.0);
assertEquals(10_000.0, bsu.getBalance(), epsilon, "The account balance was incorrect");
assertThrows(IllegalArgumentException.class, () -> {
bsu.deposit(-100.0);
}, "Negative deposit should have triggered an IllegalArgumentException!");
assertThrows(IllegalStateException.class, () -> {
bsu.deposit(20_000.0);
}, "Should not be able to deposit more money than the deposit limit");
bsu.endYearUpdate();
bsu.deposit(20_000.0);
assertEquals(10_000.0 * (1 + 0.05) + 20_000.0, bsu.getBalance(), epsilon,
"The account balance was incorrect");
}
@Test
@DisplayName("Check that withdrawals work as expected")
public void testWithdraw() {
bsu.deposit(20_000.0);
bsu.withdraw(5000.0);
assertEquals(15_000.0, bsu.getBalance(), epsilon, "The account balance was incorrect");
assertThrows(IllegalArgumentException.class, () -> {
bsu.withdraw(-10_000.0);
}, "Negative withdrawal should have triggered an IllegalArgumentException!");
assertEquals(15_000.0, bsu.getBalance(), epsilon, "The account balance was incorrect");
assertThrows(IllegalStateException.class, () -> {
bsu.withdraw(20_000);
}, "Should not be able to withdraw more money than deposited this year");
assertEquals(15_000.0, bsu.getBalance(), epsilon, "The account balance was incorrect");
bsu.endYearUpdate();
assertThrows(IllegalStateException.class, () -> {
bsu.withdraw(10_000);
}, "Should not be able to withdraw more money than deposited this year");
assertEquals(15_000 * (1 + 0.05), bsu.getBalance(), epsilon,
"The account balance was incorrect");
}
@Test
@DisplayName("Check that the tax deduction is correct")
public void testTaxDeduction() {
bsu.deposit(20_000.0);
assertEquals(20_000.0 * 0.20, bsu.getTaxDeduction(), epsilon,
"The tax deduction was incorrect");
bsu.endYearUpdate();
bsu.deposit(10_000.0);
assertEquals(10_000.0 * 0.20, bsu.getTaxDeduction(), epsilon,
"The tax deduction was incorrect");
bsu.endYearUpdate();
assertEquals(0.0, bsu.getTaxDeduction(), epsilon, "The tax deduction was incorrect");
}
}

View File

@@ -0,0 +1,72 @@
package oving7.savingsaccount;
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 ForeldreSparTest {
private static final double epsilon = 0.001;
private ForeldreSpar foreldreSpar;
@BeforeEach
public void setUp() {
foreldreSpar = new ForeldreSpar(0.04, 3);
}
@Test
@DisplayName("Check that withdraw works as expected")
public void testWithdraw() {
foreldreSpar.deposit(10_000.0);
foreldreSpar.withdraw(1000.0);
assertEquals(9000.0, foreldreSpar.getBalance(), epsilon,
"The account balance is incorrect");
assertThrows(IllegalArgumentException.class, () -> {
foreldreSpar.withdraw(-10_000.0);
}, "Negative withdrawal should have triggered an IllegalArgumentException!");
assertEquals(9000, foreldreSpar.getBalance(), epsilon, "The account balance is incorrect");
assertThrows(IllegalStateException.class, () -> {
foreldreSpar.withdraw(10_000);
}, "Should not be able to withdraw more money than is in the account");
assertEquals(9000.0, foreldreSpar.getBalance(), epsilon,
"The account balance is incorrect");
foreldreSpar.withdraw(1000.0);
foreldreSpar.withdraw(1000.0);
assertThrows(IllegalStateException.class, () -> {
foreldreSpar.withdraw(1000.0);
}, "Should not be able to make more withdrawals than the set limit");
foreldreSpar.endYearUpdate();
foreldreSpar.withdraw(1000.0);
assertEquals(7000.0 * (1 + 0.04) - 1000.0, foreldreSpar.getBalance(), epsilon,
"The account balance is incorrect");
}
@Test
@DisplayName("Check that remaining withdrawals are always correct")
public void testRemainingWithdrawals() {
foreldreSpar.deposit(10_000.0);
foreldreSpar.withdraw(1000.0);
assertEquals(2, foreldreSpar.getRemainingWithdrawals());
foreldreSpar.withdraw(1000.0);
foreldreSpar.withdraw(1000.0);
assertEquals(0, foreldreSpar.getRemainingWithdrawals());
assertThrows(IllegalStateException.class, () -> {
foreldreSpar.withdraw(1000.0);
}, "Should not be able to make more withdrawals than the set limit");
foreldreSpar.endYearUpdate();
assertEquals(3, foreldreSpar.getRemainingWithdrawals());
}
}

View File

@@ -0,0 +1,76 @@
package oving7.savingsaccount;
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 SavingsAccountTest {
private static final double epsilon = 0.001;
private SavingsAccount savingsAccount;
@BeforeEach
public void setUp() {
savingsAccount = new SavingsAccount(0.1);
}
@Test
@DisplayName("Check that the balance is correct after deposit")
public void testBalance() {
savingsAccount.deposit(100.0);
assertEquals(100.0, savingsAccount.getBalance(), epsilon,
"The account balance was incorrect");
}
@Test
@DisplayName("Test deposit and withdraw")
public void testDepositAndWithdraw() {
savingsAccount.deposit(100.0);
savingsAccount.withdraw(40.0);
assertEquals(60.0, savingsAccount.getBalance(), epsilon,
"The account balance was incorrect");
}
@Test
@DisplayName("Test deposit and withdraw with illegal input")
public void testDepositAndWithdrawIllegalInput() {
savingsAccount.deposit(10.0);
assertThrows(IllegalArgumentException.class, () -> {
savingsAccount.deposit(-100.0);
}, "Negative deposit should have triggered an IllegalArgumentException!");
assertEquals(10.0, savingsAccount.getBalance(), epsilon,
"The account balance was incorrect");
savingsAccount.deposit(10.0);
assertThrows(IllegalArgumentException.class, () -> {
savingsAccount.withdraw(-100.0);
}, "Negative withdrawal should have triggered an IllegalArgumentException!");
assertEquals(20.0, savingsAccount.getBalance(), epsilon,
"The account balance was incorrect");
savingsAccount.deposit(10.0);
assertThrows(IllegalStateException.class, () -> {
savingsAccount.withdraw(40.0);
}, "Withdrawal of more than the balance should have triggered an IllegalStateException");
assertEquals(30.0, savingsAccount.getBalance(), epsilon,
"The account balance was incorrect");
}
@Test
@DisplayName("Test that the interest is calculated correctly")
public void endYearUpdate() {
savingsAccount.deposit(100.0);
savingsAccount.endYearUpdate();
assertEquals(100.0 * (1 + 0.10), savingsAccount.getBalance(), epsilon,
"The account balance was incorrect after interest was added");
}
}

View File

@@ -0,0 +1,28 @@
package oving7.train;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
public class CargoCarTest {
private CargoCar cargoCar;
@BeforeEach
public void setUp() {
cargoCar = new CargoCar(3000, 2000);
}
@Test
@DisplayName("Check total weight")
public void testWeight() {
assertEquals(5000, cargoCar.getTotalWeight(), "Test total weight after initialization");
cargoCar.setCargoWeight(4000);
assertEquals(7000, cargoCar.getTotalWeight(),
"Test total weight after changing cargo weight");
assertEquals(4000, cargoCar.getCargoWeight(),
"Test cargo weight after changing the weight");
}
}

View File

@@ -0,0 +1,29 @@
package oving7.train;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
public class PassengerCarTest {
private PassengerCar passengerCar;
@BeforeEach
public void setUp() {
passengerCar = new PassengerCar(3000, 200);
}
@Test
@DisplayName("Check total weight")
public void testWeight() {
assertEquals(3000 + (200 * 80), passengerCar.getTotalWeight(),
"Test total weight after initialization");
passengerCar.setPassengerCount(100);
assertEquals(3000 + (100 * 80), passengerCar.getTotalWeight(),
"Test total weight after changing the number of passengers");
assertEquals(100, passengerCar.getPassengerCount(),
"Test passenger count after changing the number");
}
}

View File

@@ -0,0 +1,26 @@
package oving7.train;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
public class TrainCarTest {
private TrainCar trainCar;
@BeforeEach
public void setUp() {
trainCar = new TrainCar(3000);
}
@Test
@DisplayName("Dead weight equals total weight")
public void testDeadWeight() {
assertEquals(3000, trainCar.getTotalWeight(), "Test initialization of dead weight");
trainCar.setDeadWeight(5000);
assertEquals(5000, trainCar.getTotalWeight(),
"Test that total weight equals set dead weight");
}
}

View File

@@ -0,0 +1,85 @@
package oving7.train;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
public class TrainTest {
private CargoCar cc1;
private CargoCar cc2;
private PassengerCar pc1;
private PassengerCar pc2;
private Train train;
@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("Add cars to train")
public void testAddCarToTrain() {
train.addTrainCar(pc1);
train.addTrainCar(pc2);
train.addTrainCar(cc1);
assertTrue(train.contains(pc1),
"Test if the train contains passenger car 1 after it has been added");
assertTrue(train.contains(pc2),
"Test if the train contains passenger car 2 after it has been added");
assertTrue(train.contains(cc1),
"Test if the train contains cargo car 1 after it has been added");
assertFalse(train.contains(cc2),
"Test if the train contains cargo car 2 without it being added");
assertThrows(IllegalArgumentException.class, () -> {
train.addTrainCar(null);
}, "Test if an IllegalArgumentException is thrown when adding a null car");
}
@Test
@DisplayName("Check total weight of the train")
public void testTotalTrainWeight() {
train.addTrainCar(pc1);
train.addTrainCar(cc1);
assertEquals(8000 + (2000 + (200 * 80)), train.getTotalWeight(),
"Test the train's total weight after adding a passenger car and a cargo car");
train.addTrainCar(pc2);
assertEquals(8000 + (2000 + (200 * 80)) + (1500 + (100 * 80)), train.getTotalWeight(),
"Test the train's total weight after adding another passenger car");
}
@Test
@DisplayName("Check passenger count on the train")
public void testPassengerCount() {
train.addTrainCar(pc1);
train.addTrainCar(pc2);
assertEquals(300, train.getPassengerCount(),
"Test passenger count after adding passenger cars");
train.addTrainCar(cc1);
assertEquals(300, train.getPassengerCount(),
"Test passenger count after adding a cargo car");
}
@Test
@DisplayName("Check cargo weight on the train")
public void testCargoWeight() {
train.addTrainCar(cc1);
train.addTrainCar(cc2);
assertEquals(12_000, train.getCargoWeight(), "Test cargo weight after adding cargo cars");
train.addTrainCar(pc1);
assertEquals(12_000, train.getCargoWeight(),
"Test cargo weight after adding a passenger car");
}
}