29 Commits

Author SHA1 Message Date
vegardbm 3f7ea46a2f oppgave: train 2026-03-23 18:36:37 +01:00
vegardbm fd661adfc5 oppgave: abstractaccount 2026-03-23 17:54:35 +01:00
vegardbm fe2266656f oppgave: savingsaccount 2026-03-20 14:48:22 +01:00
vegardbm c79555e133 move tests for tasks not implemented 2026-03-17 11:31:51 +01:00
vegardbm 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
vegardbm 04a598a5fa oppgave: logger 2026-03-03 06:31:50 +01:00
vegardbm 341848f3e8 oppgave: named 2026-03-03 05:26:25 +01:00
vegardbm 00f8009e0d oppgave: highscorelist 2026-03-03 04:22:55 +01:00
vegardbm 0dfde2ddfb oving 5 2026-03-03 01:47:32 +01:00
vegardbm 86a691b044 maven compiles all tests independetly of which tests are run 2026-03-03 01:02:36 +01:00
vegardbm f2db063e5c Merge branch 'main' 2026-02-26 22:24:51 +01:00
Andreas Omholt Olsen 6a27364518 Merge pull request #7 from tdt4100/add-oving6
Add oving 6
2026-02-23 12:14:34 +01:00
Andreas Omholt Olsen 57adabe5be Add oving 6 2026-02-23 12:10:01 +01:00
Andreas Omholt Olsen 1a6645482b Add debugging for oving5 2026-02-15 17:13:35 +01:00
vegardbm 1560d8fa25 add junit support 2026-02-10 08:34:13 +01:00
vegardbm 361cee798c oving4 2026-02-09 20:07:16 +01:00
vegardbm 661cb35325 Merge branch 'main' 2026-02-09 17:08:59 +01:00
vegardbm 8e08785db0 øving3 2026-02-09 17:03:32 +01:00
Andreas Omholt Olsen 250553c438 Merge pull request #6 from tdt4100/add-oving5
Add oving 5
2026-02-09 15:30:55 +01:00
Andreas Omholt Olsen 55c36a603a Add oving 5 2026-02-09 15:28:09 +01:00
vegardbm af8cbf6768 update nix development shell 2026-02-04 01:40:21 +01:00
Andreas Omholt Olsen 6193e26ba1 Merge pull request #5 from tdt4100/add-oving4
Add oving 4
2026-02-02 11:00:40 +01:00
Andreas Omholt Olsen 7dd68c1ed8 Add oving 4 2026-02-02 10:57:55 +01:00
vegardbm a732c16c12 initial solutions 2026-01-30 01:44:23 +01:00
216 changed files with 8402 additions and 51 deletions
+2 -1
View File
@@ -14,4 +14,5 @@ target/
out/ out/
# JDT-specific (Eclipse Java Development Tools) # JDT-specific (Eclipse Java Development Tools)
# .classpath .classpath
.project
+4
View File
@@ -8,3 +8,7 @@ Denne mappen inneholder øvingstekster for TDT4100 - Objektorientert programmeri
| [Øving 1](./oppgavetekster/oving1/README.md) | Java-syntaks og objektorientert tankegang | | [Øving 1](./oppgavetekster/oving1/README.md) | Java-syntaks og objektorientert tankegang |
| [Øving 2](./oppgavetekster/oving2/README.md) | Innkapsling og validering | | [Øving 2](./oppgavetekster/oving2/README.md) | Innkapsling og validering |
| [Øving 3](./oppgavetekster/oving3/README.md) | Klasser og testing | | [Øving 3](./oppgavetekster/oving3/README.md) | Klasser og testing |
| [Ø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 |
Generated
+27
View File
@@ -0,0 +1,27 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1770141374,
"narHash": "sha256-yD4K/vRHPwXbJf5CK3JkptBA6nFWUKNX/jlFp2eKEQc=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "41965737c1797c1d83cfb0b644ed0840a6220bd1",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}
+20
View File
@@ -0,0 +1,20 @@
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
};
outputs =
{ nixpkgs, ... }:
let
system = "x86_64-linux";
pkgs = import nixpkgs { inherit system; };
in
{
devShells.${system}.default = pkgs.mkShell {
packages = with pkgs; [
maven
javaPackages.compiler.openjdk25
];
};
};
}
+26
View File
@@ -0,0 +1,26 @@
# Objektstrukturer - Card-oppgave del 2
Denne oppgaven handler om klasser for kortspill: `Card` (kort), `CardDeck` (kortstokk) og `CardHand` (korthånd), hvorav de to siste inneholder én eller flere `Card`-objekter. Oppgaven bygger på `Card` og `CardDeck` i [Innkapsling - Card-oppgave](../oving3/Card.md).
Filene i denne oppgaven skal lagres i [oving4/card](../../src/main/java/oving4/card).
**Merk**: Om du ikke har gjort `Card`-oppgaven allerede, bør du gjøre dette 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/oving3/card/Card.java), som kommer til å være tilgjengelig etter siste demonstrasjonsfrist for øving 3.
I mange sammenhenger vil objekter av en klasse inneholde eller "eie" objekter av andre klasser, og de underordnede objektene vil kunne flyttes/overføres mellom de overordnede. Når en klasse er assosiert med én instans av en (annen) klasse er dette en [1-1-assosiasjon](https://www.ntnu.no/wiki/display/tdt4100/Koding+av+1-1-assosiasjoner) og når en klasse er assosiert med flere instanser av en annen klasse er dette en [1-n-assosiasjon](https://www.ntnu.no/wiki/display/tdt4100/Koding+av+1-n-assosiasjoner). Et eksempel er kortspill, hvor kortene starter i kortstokken, fordeles på korthender og til slutt ender i en kortbunke. Et kort kan bare være ett sted om gangen, og må overføres fra ett sted til et annet, f.eks. fra kortstokk til korthender i utdelingsfasen. I [Innkapsling - Card-oppgave](../oving3/Card.md) ble det lagd logikk for kortstokk og enkeltkort. I denne oppgaven skal du implementere logikk for korthender, og utvide kortstokkens logikk litt.
`Card`-klassen har du allerede implementert, men du må sannsynligvis kopiere koden over fra `oving3` til `oving4`. Her er det enklest å lage en ny `Card`-klasse i `oving4` og så lime inn innholdet fra den gamle. Husk å ha riktig `package`.
`CardDeck`-klassen har du også implementert, og denne må også flyttes på samme måte som `Card`. Denne klassen skal utvides:
- `void deal(CardHand cardHand, int n)` - flytter `n` kort fra kortstokken (`CardDeck`-objektet) til korthånda (`CardHand`-objektet, som er første argument), ved å ta ett og ett kort med høyeste gyldige indeks, fjerne det fra `CardDeck`-objektet og legge det til `CardHand`-objektet. Kast unntak av typen `IllegalArgumentException` dersom `cardHand` er `null`.
`CardHand` er en ny klasse som skal implementeres. `CardHand`-objekter inneholder initielt ingen kort, og klassen inneholder de samme standardmetodene som `CardDeck`, altså `getCardCount()` og `getCard(int)`, for å lese hvor mange og hvilke kort den inneholder. I tillegg har den to metoder for å endre tilstand:
- `void addCard(Card)` - legger argumentet til dette `CardHand`-objektet. Kast unntak av typen `IllegalArgumentException` dersom argumentet er `null`.
- `Card play(int n)` - returnerer og fjerner kort nr. `n` (første kort har nr. $0$) fra dette `CardHand`-objektet (som om det ble spilt ut).
## Java-kode
Utvid `CardDeck` og lag `CardHand` som beskrevet over. Test klassene med selvlagde main-metoder og ved å kjøre JUnit-testene.
Testkode for denne oppgaven finner du her: [oving4/card/CardTest.java](../../src/test/java/oving4/card/CardTest.java), [oving4/card/CardDeckTest.java](../../src/test/java/oving4/card/CardDeckTest.java), [oving4/card/CardHandTest.java](../../src/test/java/oving4/card/CardHandTest.java).
+62
View File
@@ -0,0 +1,62 @@
# Objektstrukturer - Partner-oppgave
Denne oppgaven handler om en `Partner`-klasse med en [1-1-assosiasjon](https://www.ntnu.no/wiki/display/tdt4100/Koding+av+1-1-assosiasjoner) kalt partner tilbake til samme klasse (altså kjønnsnøytralt partnerskap) og det å sikre konsistens, slik at Partner-objekter er parvis knyttet sammen.
En viktig del av det å implementere assosiasjoner er å sikre konsistens, dvs. at objekter i hver ende av en kobling refererer korrekt til hverandre. Et eksempel på dette for [1-1-assosiasjoner](https://www.ntnu.no/wiki/display/tdt4100/Koding+av+1-1-assosiasjoner) er (kjønnsnøytralt) partnerskap, hvor to partnere er koblet til hverandre når partnerskap inngås og kobles fra hverandre ved en evt. skillsmisse. I denne oppgaven skal en `Partner`-klasse implementeres og ulike situasjoner håndteres korrekt, som illustrert nedenfor.
`Partner`-klassen skal inneholde informasjon om _navn_ (en `String` ulik `null`), som bare skal kunne settes i konstruktøren, og _partneren_, som er et annet `Partner`-objekt. Navnet er ikke viktig for oppførselen, men er grei å ha med i en `toString()`-metode, for å skille `Partner`-objektene fra hverandre. `Partner`-klassen skal ha følgende metoder for å lese tilstanden:
- `String getName()` - returnerer navnet knyttet til dette `Partner`-objektet.
- `Partner getPartner()` - returnerer `Partner`-objektet som er knyttet til dette `Partner`-objektet, evt. `null`, hvis partnerskap ikke er inngått.
`Partner`-klassen har kun én endringsmetode, `void setPartner(Partner)`, som brukes både for å inngå partnerskap, når argumentet er et `Partner`-objekt, og oppløse det, når argumentet er `null`. Figurene under illustrerer de tre tilfellene som må kunne håndteres, og som `JUnit`-testene sjekker.
## Eksempler på kall
### 1. Inngåelse av partnerskap
**Kall**: `p1.setPartner(p2)`
**Beskrivelse**: `Partner`-objektene `p1` og `p2` kobles sammen med ett kall til `setPartner`. Før kallet er `p1` og `p2` ikke koblet sammen, og etter kallet er det koblet sammen.
**Før kall**:
![partner1](./img/partner1.png)
**Etter kall**:
![partner2](./img/partner2.png)
### 2. Oppløsning av partnerskap
**Kall**: `p1.setPartner(null)`
**Beskrivelse**: `Partner`-objektene `p1` og `p2` kobles fra hverandre med ett kall til `setPartner` med `null` som argument. Før kallet er `p1` og `p2` koblet sammen, og etter kallet er det ikke lenger koblet sammen.
**Før kall**:
![partner2](./img/partner2.png)
**Etter kall**:
![partner1](./img/partner1.png)
### 3. Oppløsning og inngåelse av partnerskap i ett
**Kall**: `p1.setPartner(p3)`
**Beskrivelse**: `Partner`-objektene `p1`, `p2`, `p3` og `p4` er parvis koblet sammen, før ett kall til `setPartner` kobler sammen `p1` og `p3`, mens `p2` og `p4` kobles fra deres tidligere partnere.
**Før kall**:
![partner3](./img/partner3.png)
**Etter kall**:
![partner4](./img/partner4.png)
## Gjøremål
Oppgaven er (enkelt og greit) å implementere `Partner`-klassen og sjekke (f.eks. med en `main()`-metode) at `Partner`-objektene oppfører seg som de skal.
Testkode for denne oppgaven finner du her: [oving4/PartnerTest.java](../../src/test/java/oving4/PartnerTest.java).
+134
View File
@@ -0,0 +1,134 @@
# Objektstrukturer - Person-oppgave
Denne oppgaven handler om en `Person`-klasse med en [1-n-assosiasjon](https://www.ntnu.no/wiki/display/tdt4100/Koding+av+1-n-assosiasjoner) med rollene _children_ og _mother_/_father_ til samme klasse (altså barn-mor/far-forhold) og det å sikre konsistens, slik at foreldre og barn er korrekt knyttet sammen.
En viktig del av det å implementere assosiasjoner er å sikre konsistens, dvs. at objekter i hver ende av en kobling refererer korrekt til hverandre. Et eksempel på dette for [1-n-assosiasjoner](https://www.ntnu.no/wiki/display/tdt4100/Koding+av+1-n-assosiasjoner) er foreldreskap, hvor foreldre og barn er koblet til samme i et slektstre. I denne oppgaven skal en `Person`-klasse implementeres og det å legge til (og fjerne) barn håndteres korrekt, som illustrert nedenfor.
`Person`-klassen skal inneholde informasjon om _navn_ (en `String` ulik `null`) og _kjønn_ (en `char`, `'F'` eller `'M'`), som bare skal kunne settes i konstruktøren, og _mor_, _far_ og _barn_, som er andre `Person`-objekter. Navnet er ikke viktig for oppførselen, men er grei å ha med i en `toString()`-metode, for å skille `Person`-objektene fra hverandre. `Person`-klassen skal ha følgende metoder for å lese tilstanden:
- `String getName()` - returnerer navnet knyttet til dette `Person`-objektet.
- `char getGender()` - returnerer tegnet som representerer kjønnet, enten `'F'` eller `'M'`.
- `Person getMother()` - returnerer `Person`-objektet som er moren, evt. `null`.
- `Person getFather()` - returnerer `Person`-objektet som er faren, evt. `null`.
- `int getChildCount()` - returnerer antall barn dette `Person`-objektet har.
- `Person getChild(int n)` - returnerer barn nr. `n` (altså et `Person`-objekt), evt. utløser (et passende) unntak om `n` er for stor (eller liten).
`Person`-klassen har to sett med endringsmetoder, knyttet til de to rollene i hver ende av _children_-_mother_/-_father_-assosiasjonene.
Fra _children_-perspektivet har vi følgende to metoder:
- `void addChild(Person)` - oppretter en kobling til et barn (et annet `Person`-objekt). Dersom `Person`-objektet som metoden kalles på, er en _kvinne_, så skal denne bli barnets _mor_, og motsatt, dersom `Person`-objektet som metoden kalles på, er en _mann_, så skal denne bli barnets _far_. Argumentet kan ikke være `null`.
- `void removeChild(Person)` - fjerner en kobling til et barn (et annet `Person`-objekt). Dersom `Person`-objektet som metoden kalles på, er _moren_ til argumentet, så skal _mother_-koblingen fjernes, og motsatt, dersom `Person`-objektet som metoden kalles på, er argumentets _far_, så skal _father_-koblingen fjernes.
Fra _mother_/_father_-perspektivet har vi følgende to metoder:
- `void setMother(Person)` - setter argumentet (en kvinne) som _moren_ til `Person`-objektet som metoden kalles på. Argumentet får samtidig registrert `Person`-objektet som metoden kalles på, som sitt _barn_.
- `void setFather(Person)` - setter argumentet (en mann) som _faren_ til `Person`-objektet som metoden kalles på. Argumentet får samtidig registrert `Person`-objektet som metoden kalles på, som sitt _barn_.
Det som er verd å merke seg er at begge sett med metoder, `addChild`/`removeChild` og `setMother`/`setFather`, må ha logikk som håndterer koblingen den andre veien, så `addChild`/`removeChild` må kalle `setMother`/`setFather` og omvendt, eller ha kode med tilsvarende effekt. Dette kan være nokså fiklete, fordi en både må sikre konsistens og unngå uendelig nøstede kall (inntil du får `StackOverflowException`).
Listen og figurene under illustrerer de fem tilfellene som må kunne håndteres, og som testes av testene det er lenket til.
## Eksempler på kall
### 1. Opprettelse av koblinger med `addChild`
**Kall**:
`marit.addChild(jens)`
`hallvard.addChild(jens)`
(Dette har samme effekt som kallene under punkt [2](#2-opprettelse-av-koblinger-med-setmother-og-setfather).)
**Før kall**:
![person1](./img/person1.png)
**Etter kall**:
![person2](./img/person2.png)
### 2. Opprettelse av koblinger med `setMother` og `setFather`
**Kall**:
`jens.setMother(marit)`
`jens.setFather(hallvard)`
(Dette har samme effekt som kallene under punkt [1](#1-opprettelse-av-koblinger-med-addchild).)
**Før kall**:
![person1](./img/person1.png)
**Etter kall**:
![person2](./img/person2.png)
### 3. Fjerning av koblinger med `removeChild`
**Kall**:
`marit.removeChild(jens)`
`hallvard.removeChild(jens)`
(Dette har samme effekt som kallene under punkt [4](#4-fjerning-av-koblinger-med-setmother-og-setfather).)
**Før kall**:
![person2](./img/person2.png)
**Etter kall**:
![person1](./img/person1.png)
### 4. Fjerning av koblinger med `setMother` og `setFather`
**Kall**:
`jens.setMother(null)`
`jens.setFather(null)`
(Dette har samme effekt som kallene under punkt [3](#3-fjerning-av-koblinger-med-removechild).)
**Før kall**:
![person2](./img/person2.png)
**Etter kall**:
![person1](./img/person1.png)
### 5. Fjerning og oppretting av kobling med `setMother` og `setFather`, en slags "adoption"
**Kall**:
`jens.setFather(torkel)`
`jens.setMother(jorunn)`
**Før kall**:
![person3](./img/person3.png)
**Etter kall**:
![person4](./img/person4.png)
## Oppgaven
Oppgaven er delt i to trinn, den første håndterer _children_- og _mother_/_father_-rollen isolert og uten krav om konsistens,
mens det andre skal sikre konsistens.
### Del 1
- Implementer `addChild`- og `removeChild`-metodene slik at `getChildCount`- og `getChild`-metodene virker som forventet. Disse metodene håndterer altså kun _children_-rollen.
- Implementer `setMother`- og `setFather`-metodene slik at `getMother`- og `getFather`-metodene virker som forventet. Disse metodene håndteres altså kun _mother_/_father_-rollen.
Test metodene ved å koble opp `Person`-objekter tilsvarende din egen familie. Du blir nødt til å bruke de tre metodene `addChild`, `setMother` og `setFather`. Prøv å få med minst tre generasjoner.
### Del 2
Utvid metodene til å sikre konsistens. Test at det fortsatt virker å koble opp din egen familie, denne gangen ved å bare bruke
`addChild` og ved å bare bruke `setMother` og `setFather`. Kjør `JUnit`-testene som hører til oppgaven.
Testkode for denne oppgaven finner du her: [oving4/PersonTest.java](../../src/test/java/oving4/PersonTest.java).
+61
View File
@@ -0,0 +1,61 @@
# Øving 4: Objektstrukturer
## Øvingsmål
- Lære hva assosiasjoner er og hvordan dette brukes i OO
- Lære hvordan man sikrer konsistent oppførsel mellom assosierte objekter
## Øvingskrav
- Kunne implementere klasser som har assosiasjoner til én eller flere andre klasser
- Kunne sikre at disse assosiasjon er konsistente i enkle objektstrukturer
- Kunne implementere metoder som oppretter, oppdaterer og fjerner slike assosiasjoner
## Dette må du gjøre
### Del 1: Programmering
I denne øvingen skal du velge og gjennomføre ENTEN både Partner- og Card del 2-oppgavene ELLER minst én av Twitter-, Stopwatch- og Person-oppgavene. Merk at **noen av oppgavene i neste øving (øving 5), bygger videre på noen av oppgavene under**, disse er uthevet med **fet skrift**. Det er ikke et krav at man gjør de uthevede oppgavene, men de gir flere oppgaver å velge mellom i øving 6.
**Gjør enten _begge_ disse:**
- [Partner](./Partner.md) (lett)
- **[Card del 2](./Card.md)** (lett)
**Eller _minst én_ av følgende oppgaver:**
- **[Twitter](./Twitter.md)** (medium, men lang)
- [Stopwatch](./StopWatch.md) (medium)
- [Person](./Person.md) (medium/vanskelig)
Oppgavene for denne øvingen skal du lagre i [`src/main/java/oving4`](../../src/main/java/oving4). Test-filene ligger i [`src/test/java/oving4`](../../src/test/java/oving4).
Alle oppgavene ovenfor er høyst eksamensrelevante og det anbefales å ta en titt på alle sammen.
### Del 2: Klassediagram
- Lag et [klassediagram](https://www.ntnu.no/wiki/display/tdt4100/Klassediagrammer) for en av oppgavene du velger. Husk å få med relasjonene mellom klassene.
Diagrammet kan for eksempel skrives på papir eller tegnes/lages i et valgfritt program. Du trenger ikke levere inn diagrammene, men de skal vises til studass under godkjenning av øvingen.
### Del 3: Testing
Skriv kode som tester oppførselen til `CoffeeCup`-klassen, dvs. at du skal teste om metodene i listen under har rett oppførsel og returnerer det de skal, i tillegg til at du skal teste konstruktørene. Det er ikke nødvendig å teste absolutt alle mulige tilfeller, men det kreves at du tester den grunnleggende funksjonaliteten. Basert på resultatene dine, vurder om klassen er implementert på en logisk måte.
- `double getCapacity()`
- `double getCurrentVolume()`
- `void increaseCupSize(double)`
- `void drinkCoffee(double)`
- `void fillCoffee(double)`
Du finner `CoffeeCup`-klassen under [`src/main/java/oving4/testing`](../../src/main/java/oving4/testing).
Her er det anbefalt å bruke [JUnit](https://www.ntnu.no/wiki/display/tdt4100/Enhetstesting+med+JUnit), men det er lov å teste vha. `main()`-metoden også. Dersom du bruker JUnit må du opprette testen under navnet `CoffeeCupTest` i [`src/test/java/oving4/testing`](../../src/test/java/oving4/testing).
### 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.
+34
View File
@@ -0,0 +1,34 @@
# Objektstrukturer - StopWatchManager-oppgave
Denne oppgaven handler om en `StopWatchManager`-klasse som inneholder flere `StopWatch`-objekter. Oppgaven bygger på klassen lagd i [StopWatch-oppgaven](../oving1/Stopwatch.md) fra "tilstand og oppførsel".
Dersom du ikke har gjort [`StopWatch`-oppgaven](../oving1/Stopwatch.md) allerede, bør du gjøre denne 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/oving1/StopWatch.java), som kommer til å være tilgjengelig etter siste demonstrasjonsfrist for øving 1. Merk at validering i `StopWatch`-klassen ble gjort i [Innkapsling](../oving2/Encapsulation.md).
Filene i denne oppgaven skal lagres i [oving4/stopwatch](../../src/main/java/oving4/stopwatch).
I mange sammenhenger vil objekter av en klasse inneholde eller "eie" objekter av andre klasser. Når en klasse er assosiert med én instans av en (annen) klasse er dette en [1-1-assosiasjon](https://www.ntnu.no/wiki/display/tdt4100/Koding+av+1-1-assosiasjoner) og når en klasse er assosiert med flere instanser av en annen klasse er dette en [1-n-assosiasjon](https://www.ntnu.no/wiki/display/tdt4100/Koding+av+1-n-assosiasjoner).
I denne oppgaven skal du implementere en `StopWatchManager`-klasse som kan holde styr på flere stoppeklokker. Ved hjelp av `StopWatchManager` skal man enkelt kunne holde styr på flere stoppeklokker og sørge for at alle stoppeklokker får beskjed om tiden som går. Dette kan være nyttig hvis man f.eks. ønsker å holde styr på flere løpere i et skirenn der ikke alle starter og fullfører samtidig, men hvor allikevel klokken må gå for alle.
Det skal være mulig å opprette nye stoppeklokker med et tilhørende navn (streng). Navnet skal man senere kunne bruke til å hente stoppeklokken igjen eller fjerne stoppeklokken fra `StopWatchManager`. For å få til dette kan det være lurt å se litt på `Map` fra [Collection-rammeverket](https://www.ntnu.no/wiki/display/tdt4100/Collection-rammeverket).
`StopWatchManager` skal ha følgende endringsmetoder:
- `StopWatch newStopWatch(String name)` - Oppretter en ny stoppeklokke knyttet til navnet `name`. Returnerer den nye stoppeklokken. Argumentet kan ikke være `null` eller allerede være knyttet til en stoppeklokke.
- `void removeStopWatch(String name)` - Fjerner stoppeklokken tilknyttet navnet `name`.
- `void tick(int ticks)` - Informerer alle stoppeklokkene om at ticks tikk har gått.
`StopWatchManager` skal ha følgende lesemetoder:
- `StopWatch getStopWatch(String name)` - returnerer stoppeklokken tilknyttet navnet `name`.
- `Collection<StopWatch> getAllWatches()` - returnerer alle stoppeklokkene.
- `Collection<StopWatch> getStartedWatches()` - returnerer alle stoppeklokkene som er startet.
- `Collection<StopWatch> getStoppedWatches()` - returnerer alle stoppeklokkene som er stoppet.
**Merk**: Det er viktig at de metodene som returnerer en samling av stoppeklokker returnerer nye samlinger. De som får en samling må kunne endre på den (f.eks. fjerne elementer) uten at dette forstyrrer `StopWatchManager` eller andre som har fått samlinger tidligere.
## Java-kode
Kopier `StopWatch` fra `oving2`-pakken og lag `StopWatchManager` som beskrevet over. Test klassen med selvlagde `main()`-metoder og ved å kjøre `JUnit`-testene.
Testkode for denne oppgaven finner du her: [oving4/stopwatch/StopWatchTest.java](../../src/test/java/oving4/stopwatch/StopWatchTest.java) og [oving4/stopwatch/StopWatchManagerTest.java](../../src/test/java/oving4/stopwatch/StopWatchManagerTest.java).
+67
View File
@@ -0,0 +1,67 @@
# Objektstrukturer - Twitter-oppgave
Denne oppgaven handler om en begrenset klone av `Twitter`, med to klasser, `TwitterAccount` og `Tweet`.
Filene i denne oppgaven skal lagres i [oving4/twitter](../../src/main/java/oving4/twitter).
En `Twitter`-konto kan følge andre `Twitter`-kontoer og motsatt: en `Twitter`-konto kan bli fulgt av andre `Twitter`-kontoer.
Dette er altså en gjensidig kobling: Hvis konto A følger konto B, så er konto B fulgt av konto A. En kan selvsagt ikke følge seg selv.
I tillegg har hver Twitter-konto en mengde _tweets_, som er små, korte tekster. En tweet hører til den kontoen den ble sendt fra. Hvis noen finner en annen sin tweet interessant har man muligheten til å retweete denne. Da lager man en tweet som refererer til originalen, og (implisitt) få original-tweeten sin tekst. Merk at i en kjede av retweets, så vil alle referere til samme original-tweet. Med andre ord, hvis tweet B er en retweet av A og tweet C er en retweet av B, vil både tweet B og C ha A som original-tweet, slik det er vist under.
**Riktig objektstrutur**, når B er en retweet av A og C er en retweet av B:
![twitter1](./img/twitter1.png)
**Feil objektstrutur**, når B er en retweet av A og C er en retweet av B:
![twitter2](./img/twitter2.png)
## Tweet-klassen
`Tweet` skal ha to konstruktører, en for hver måte å tweete på:
- `Tweet(TwitterAccount, String)` - En ny original-tweet. Ingen av argumentene kan være `null`.
- `Tweet(TwitterAccount, Tweet)` - En retweet av `Tweet`-argumentet. Utløser et passende unntak hvis original-tweeten er fra samme konto. Ingen av argumentene kan være `null`.
`Tweet` skal ha metodene:
- `String getText()` - returnerer teksten til en tweet.
- `TwitterAccount getOwner()` - returnerer kontoen som tweeten kom fra.
- `Tweet getOriginalTweet()` - returnerer original-tweeten, hvis den er en retweet, ellers `null`.
- `int getRetweetCount()` - returnerer antall ganger denne tweeten har blitt retweetet.
## TwitterAccount-klassen
`TwitterAccount` skal ha konstruktøren:
- `TwitterAccount(String)` - som tar inn brukernavnet.
`TwitterAccount` skal ha metodene:
- `String getUserName()` - returnerer brukernavnet.
- `void follow(TwitterAccount account)` - denne (`this`) kontoen starter å følge account. Hvis account allerede følges, skal metoden ikke gjøre noe. Dersom `account` er seg selv eller `null`, skal metoden kaste et passende unntak.
- `void unfollow(TwitterAccount account)` - slutt å følge account.
- `boolean isFollowing(TwitterAccount account)` - returnerer om denne kontoen følger account.
- `boolean isFollowedBy(TwitterAccount account)` - returnerer om account følger denne kontoen.
- `void tweet(String)` - lager en ny tweet for denne kontoen.
- `void retweet(Tweet tweet)` - retweeter tweet fra denne kontoen.
- `Tweet getTweet(int i)` - returner tweet nummer `i`, der $1$ er den nyeste, $2$ den nest nyeste, osv. (Merk rekkefølgen!)
- `int getTweetCount()` - returner antall tweets til kontoen.
- `int getRetweetCount()` - returner antall retweets av tweets fra denne kontoen.
## Del 1
- Implementer `Tweet`-klassen.
- For å teste klassen må du sende inn TwitterAccount-objekter i konstruktøren. Lag en forenklet versjon av `TwitterAccount`-klassen for dette formålet, der du kun implementerer konstruktøren og evt. en passende `toString()`-metode. Dette gjør det mulig å teste `Tweet`-klassen din uten at du må implementere hele `TwitterAccount`-klassen først.
## Del 2
- Implementer `TwitterAccount`-klassen.
- Test klassen og dens samspill med `Tweet`-klassen ved å lage Twitter-konto for deg selv og noen av vennene dine. La noen av kontoene følge hverandre, tweete og retweete.
Testkode for denne oppgaven finner du her: [oving4/twitter/TweetTest.java](../../src/test/java/oving4/twitter/TweetTest.java) og [oving4/twitter/TwitterAccountTest.java](../../src/test/java/oving4/twitter/TwitterAccountTest.java).
## Frivillig utvidelse
På Twitter kan man markere en annen sin tweet som en favoritt. Implementer passende metoder for å kunne gjøre dette. En konto må ha oversikt over hvilke tweets den har markert som favoritter, og en tweet må vite hvem og hvor mange som har markert den som favoritt. Hva synes du burde skje hvis man markerer en retweet som en favoritt?
Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

+8
View File
@@ -0,0 +1,8 @@
@startuml partner1
skinparam dpi 400
object "~#p1:Partner" as p1
object "~#p2:Partner" as p2
@enduml
Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

+11
View File
@@ -0,0 +1,11 @@
@startuml partner2
skinparam dpi 400
object "~#p1:Partner" as p1
object "~#p2:Partner" as p2
p1 --> p2 : partner
p2 --> p1 : partner
@enduml
Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

+15
View File
@@ -0,0 +1,15 @@
@startuml partner3
skinparam dpi 400
object "~#p1:Partner" as p1
object "~#p2:Partner" as p2
object "~#p3:Partner" as p3
object "~#p4:Partner" as p4
p1 --> p2 : partner
p2 --> p1 : partner
p3 --> p4 : partner
p4 --> p3 : partner
@enduml
Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

+13
View File
@@ -0,0 +1,13 @@
@startuml partner4
skinparam dpi 400
object "~#p1:Partner" as p1
object "~#p2:Partner" as p2
object "~#p3:Partner" as p3
object "~#p4:Partner" as p4
p1 --> p3 : partner
p3 --> p1 : partner
@enduml
Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

+9
View File
@@ -0,0 +1,9 @@
@startuml person1
skinparam dpi 400
object "~#hallvard:Person" as hallvard
object "~#marit:Person" as marit
object "~#jens:Person" as jens
@enduml
Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

+14
View File
@@ -0,0 +1,14 @@
@startuml person2
skinparam dpi 400
object "~#hallvard:Person" as hallvard
object "~#marit:Person" as marit
object "~#jens:Person" as jens
marit --> jens : children
jens --> marit : mother
jens --> hallvard : father
hallvard --> jens : children
@enduml
Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

+16
View File
@@ -0,0 +1,16 @@
@startuml person3
skinparam dpi 400
object "~#hallvard:Person" as hallvard
object "~#marit:Person" as marit
object "~#jens:Person" as jens
object "~#torkel:Person" as torkel
object "~#jorunn:Person" as jorunn
marit --> jens : children
jens -u-> marit : mother
jens --> hallvard : father
hallvard --> jens : children
@enduml
Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

+16
View File
@@ -0,0 +1,16 @@
@startuml person4
skinparam dpi 400
object "~#hallvard:Person" as hallvard
object "~#marit:Person" as marit
object "~#jens:Person" as jens
object "~#torkel:Person" as torkel
object "~#jorunn:Person" as jorunn
jorunn --> jens : children
jens -u-> jorunn : mother
jens --> torkel : father
torkel --> jens : children
@enduml
Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

+20
View File
@@ -0,0 +1,20 @@
@startuml twitter1
skinparam dpi 400
object "~#a:Tweet" as a {
text = "Kvitre-kvitre"
}
object "~#b:Tweet" as b {
text = "Kvitre-kvitre"
}
object "~#c:Tweet" as c {
text = "Kvitre-kvitre"
}
b --> a : originalTweet
c --> a : originalTweet
@enduml
Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

+20
View File
@@ -0,0 +1,20 @@
@startuml twitter2
skinparam dpi 400
object "~#a:Tweet" as a {
text = "Kvitre-kvitre"
}
object "~#b:Tweet" as b {
text = "Kvitre-kvitre"
}
object "~#c:Tweet" as c {
text = "Kvitre-kvitre"
}
b --> a : originalTweet
c --> b : originalTweet
@enduml
@@ -0,0 +1,45 @@
# Interface - BinaryComputingIterator
Denne oppgaven handler om en meta-iterator som kombinerer par av verdier fra to iteratorer til en ny iterator-sekvens ved hjelp av en gitt binær operasjon.
Meta-iteratorer er iteratorer som bruker andre iteratorer som datakilder.
Du skal lage en ny klasse `BinaryComputingIterator`, som er en *implementasjon* av det innebygde `Iterator<Double>`-grensesnittet. Konstruktøren til `BinaryComputingIterator` skal ta inn to iteratorer og en binær operator, samt to valgfrie standardverdier. Når `next()`-metoden til `BinaryComputingIterator` blir kalt, skal en verdi hentes fra hver av de to iteratorene, den binære operasjonen utføres på disse to verdiene og resultatet returneres. Den binære operasjonen skal spesifiseres ved hjelp av det innebygde `BinaryOperator<Double>`-grensesnittet, som har en `apply`-metode som lar en beregne resultatet av den binære operasjonen. Hvordan en kan opprette funksjonelle grensesnitt kan du lese mer om på [wikisiden om dette](https://www.ntnu.no/wiki/display/tdt4100/Lambda-uttrykk+og+funksjonelle+grensesnitt+i+Java+8).
## BinaryComputingIterator
Klassen skal ha to konstruktører, en med og en uten standardverdier:
- `BinaryComputingIterator(Iterator<Double> iterator1, Iterator<Double> iterator2, BinaryOperator<Double> operator)`. Ingen av argumentene kan være `null`.
- `BinaryComputingIterator(Iterator<Double> iterator1, Iterator<Double> iterator2, Double default1, Double default2, BinaryOperator<Double> operator)`.
Her er `iterator1` og `iterator2` iteratorene som blir kombinert av `BinaryComputingIterator`, og `default1` og `default2` er standardverdier for de respektive iteratorene. Klassen må ha følgende metoder, begge spesifisert av `Iterator<Double>`-grensesnittet: Hvordan en lager en `BinaryOperator` klasse kan du se mer om lenger ned i oppgaveteksten.
- `boolean hasNext()` - returnerer true dersom det kan beregnes flere verdier, altså hvis begge iteratorene enten har flere verdier eller har en tilhørende standardverdi. Men merk at `hasNext()` returnerer uansett `false` hvis begge iteratorene er tomme.
- `Double next()` - returnerer resultatet av å bruke binæroperasjonen operator på de neste verdiene fra sub-iteratorene, og bruker standardverdier dersom en av iteratorene ikke har flere verdier.
Husk at ved hjelp av Java 8-syntaks kan en implementasjon av BinaryOperator skrives som `(num1, num2) -> <uttrykk>`, der `<uttrykk>` er et Java-uttrykk som brukes `num1` og `num2`. Hvis BinaryComputerIterator henter verdier fra to iteratorer med hhv. verdiene `1`, `2`, `3` og `3`, `4`, `5` og den binære operatoren er `(num1, num2) -> num1 + num2`, så skal sekvensen en får ut være `4`, `6`, `8`.
For å håndtere tilfellet hvor den ene iteratoren gir ut flere verdier enn den andre, så skal det være mulig å gi standardverdier hhv. `default1` og `default2` for `iterator1` og `iterator2`, som vil bli brukt for å fylle inn manglende verdier. Hvis `BinaryComputerIterator` henter verdier fra to iteratorer med hhv. verdiene `6`, `3`, `0` og `3`, `4` og den binære operatoren er `(num1, num2) -> num1 - num2` og `default2` er `2`, så skal sekvensen en får ut være `3`, `-1`, `-2`.
### Eksempel
`BinaryOperator`-implementasjoner kan lett skrive ved hjelp av Java 8 sin funksjonsnotasjon. Dersom man for eksempel vil bruke en addisjonsoperator kan det se slik ut:
```java
Iterator<Double> iterator1 = List.of(2.0, 3.0).iterator();
Iterator<Double> iterator2 = List.of(5.0).iterator();
BinaryOperator<Double> addition = (a, b) -> a + b;
// Opprett en ny BinaryComputingIterator som tar inn iterator1 og iterator2 og utfører addisjon
// på verdiene.
BinaryComputingIterator binaryIterator = new BinaryComputingIterator(iterator1, iterator2, null, 10.0, addition);
binaryIterator.next(); // 7.0
binaryIterator.hasNext(); // true
binaryIterator.next(); // 13.0
binaryIterator.hasNext(); // false
```
Testkode for BinaryComputingIterator er her: [oving5/BinaryComputingIteratorTest.java](../../src/test/java/oving5/BinaryComputingIteratorTest.java).
+38
View File
@@ -0,0 +1,38 @@
# Interface - CardComparison-oppgave
Denne oppgaven hander om to måter å håndtere sortering av `Card`-objekter, med grensesnittene `Comparable` og `Comparator`, som er innebygd i Java (`java.util.Comparable`, `java.util.Comparator`).
Vi tar i denne oppgaven utgangspunkt i `Card`-klassen fra [Innkapsling - Card-oppgave](../oving3/Card.md). Et `Card`-objekt har en kortfarge (av typen `char`) og verdi (`int`), og sortering gjøres på disse verdiene, ved at en først sorterer på kortfarge og så på verdi. Siden Java har sorteringsmetoder innebygd trenger vi ikke bry oss om selve sorteringsalgoritmen. Vi fokuserer her på logikken for *sammenligning* av `Card`-objekter, altså hvilke `Card` som skal komme før/etter andre.
Dersom du ikke har gjort `Card`-oppgaven allerede, bør du gjøre denne 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/oving3/card/Card.java), som kommer til å være tilgjengelig etter siste demonstrasjonsfrist for øving 3.
Filen i denne oppgaven skal ligge i [`oving5/card`](../../src/main/java/oving5/card).
## Del 1
La `Card`-klassen implementere `Comparable` med følgende sammenligningslogikk
- `compareTo`-metoden skal sammenligne et kort med et annet, slik at:
- Spar kommer etter hjerter.
- Hjerter kommer etter ruter.
- Ruter kommer etter kløver.
- Ved lik kortfarge skal verdien brukes i stigende rekkefølge, altså $1$ (ess) kommer før $2$, kommer før $3$ osv. til og med $11$ (knekt), $12$ (dame) og $13$ (konge).
Skriv testkode som sorterer kort i en liste vha. `Collections.sort` og `Comparable`-logikken, og verifiser at sammenligningslogikken er riktig implementert.
*Hint:* Returner `-1` for å sette kortet `this` før kortet som blir gitt inn, `0` dersom de er like, og `1` dersom `this` skal komme etter det gitte kortet.
## Del 2
For å kunne sortere `Card`-objekter med annen logikk, så kan en bruke grensesnittet `Comparator`, som er et objekt som kan sammenligne objekter parvise. Implementer en `Comparator` (dvs. lag en klasse som *implements* `Comparator`) kalt `CardComparator`, som kan konfigureres (stilles inn) til å sortere med ess som høyeste kort og med en bestemt kortfarge som trumf, altså en kortfarge som regnes som høyere enn de andre.
- `CardComparator` må ha en konstruktør som tar inn en `boolean` og en `char`. Det første argumentet sier om ess skal regnes som størst (`true`) eller minst (`false`), mens det andre argumentet angir hvilke kortfarge som er trumf. F.eks. skal et `CardComparator`-objekt laget med `new CardComparator(true, ' ')` rangere ess høyere enn konge og bruke standard rangering av kortfarger (siden trumf-argumentet ikke er en av kortfargene), og et `CardComparator`-objekt laget med `new CardComparator(false, 'C')` rangerer ess lavest og kløver (`'C'` = clubs) høyest av kortfargene (de andre kortfargene har standard rangering med spar over hjerter over ruter).
Skriv testkode som sorterer kort i en liste vha. `Collections.sort` og `Comparator-logikken`, og verifiser at sammenligningslogikken er riktig implementert.
Testkode for del 1 og del 2 finner du her: [oving5/card/CardComparatorTest.java](../../src/test/java/oving5/card/CardComparatorTest.java).
## Valgfri Ekstraoppgave
Utvid `CardComparator` slik at den kan konfigureres med en annen rangering av kortfargene, f.eks. slik at kløver er høyere enn ruter.
Merk at denne fortsatt skal overstyres av evt. trumf. Nytten er altså at en kan endre rangeringsrekkefølgen på alle på én gang.
+30
View File
@@ -0,0 +1,30 @@
# 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](../oving4/Card.md). Her skal du lage og implementere et grensenitt kalt `CardContainer`, som spesifiserer metoder for lesing av samlinger av Card-objekter.
Dersom du ikke har gjort `Card`-oppgaven allerede, bør du gjøre denne 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/oving4/card), som kommer til å være tilgjengelig etter siste demonstrasjonsfrist for øving 4.
Filene i denne oppgaven skal ligge i [`oving5/card`](../../src/main/java/oving5/card).
## Del 1 - CardContainer interface
Definer et `CardContainer`-grensesnitt, med metodene som er felles for `CardHand` og `CardDeck`:
- `int getCardCount()` - returnerer antall kort som dette objektet inneholder.
- `Card getCard(int n)` - returnerer kort nr. `n` i dette objektet.
Gjør nødvendig endringer i `CardHand`- og `CardDeck`-klassene for å implementere `CardContainer`-grensesnittet.
## Del 2 - Iterator for CardContainer
Lag en klasse kalt `CardContainerIterator`, som er en [`Iterator`](https://www.ntnu.no/wiki/display/tdt4100/Iterasjon+med+Iterator+og+Iterable) for alle klasser som implementerer `CardContainer`-grensesnittet. `CardContainerIterator` må ha en konstruktør som tar inn en instans av (en klasse som implementerer) `CardContainer`. Du kan gjøre dette ved å ta inn et argument av typen `CardContainer`, altså `CardContainerIterator(CardContainer cardContainer) { ... }`.
*Hint*: Merk at `CardContainerIterator` ikke vet om `CardContainer`-objektet er et `CardDeck`-objekt, et `CardHand`-objekt eller et annet objekt som implementerer `CardContainer`. Den har derfor ikke tilgang til de interne listene i `CardHand` og `CardDeck`. Hvilke metoder må alle klasser som implementerer `CardContainer` ha, og hvordan kan disse metodene brukes for å lage en [`Iterator`](https://www.ntnu.no/wiki/display/tdt4100/Iterasjon+med+Iterator+og+Iterable)?
Testkode for oppgaven finner du her: [oving5/card/CardContainerIteratorTest.java](../../src/test/java/oving5/card/CardContainerIteratorTest.java).
## Del 3 - Iterable
La `CardContainer`-grensesnittet utvide (`extends`) `Iterable<Card>` og la `iterator()`-metoden som dermed kreves, returnere en korrekt konfigurert instans av `CardContainerIterator`.
Testkode for oppgaven finner du her: [oving5/card/CardDeckTest.java](../../src/test/java/oving5/card/CardDeckTest.java) og [oving5/card/CardHandTest.java](../../src/test/java/oving5/card/CardHandTest.java).
+19
View File
@@ -0,0 +1,19 @@
# Interface - CardPredicate-oppgave
Denne oppgaven handler om hvordan en kan bruke det funksjonelle `Predicate<T>`-grensesnittet, sammen med `CardDeck`-klassen. Vi tar i denne oppgaven utgangspunkt i `CardDeck`-klassen fra [Innkapsling - Card-oppgave](../oving3/Card.md). Et `CardDeck`-objekt har en liste med `Card`-objekter. `Card` har en kortfarge (av typen `char`) og verdi (`int`), og vi ønsker å lage metoder i `CardDeck` som søker opp `Card`-objekter som tilfredsstiller visse kriterier, f.eks. sjekker om spar dame finnes, teller antall hjerter eller henter ut alle ess. For å representere selve kriteriet brukes [`Predicate<T>`](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/function/Predicate.html)-grensesnittet, som handler om å teste/sjekke om et objekt tilfredsstiller visse kriterium.
Dersom du ikke har gjort `Card`-oppgaven allerede, bør du gjøre denne 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/oving3/card/Card.java), som kommer til å være tilgjengelig etter siste demonstrasjonsfrist for øving 3.
Filen i denne oppgaven skal ligge i [`oving5/card`](../../src/main/java/oving5/card).
**Implementer** følgende metoder i `CardDeck`-klassen:
- `List<Card> getCards(Predicate<Card> predicate)` - Skal returnere en liste med de kortene som tilfredsstiller `predicate`. Argumentet kan ikke være `null` for noen av metodene.
- `int getCardCount(Predicate<Card> predicate)` - Skal returnere hvor mange kort som tilfredsstiller `predicate`.
- `boolean hasCard(Predicate<Card> predicate)` - Skal returnere `true` dersom det finnes et kort som tilfredsstiller `predicate`, `false` ellers.
Lag også din egen `main()`-metode hvor du prøver hver av de tre metodene over. Du skal altså sjekke om spar dame finnes, telle antall hjerter og hente ut alle ess.
Testkode for oppgaven finner du her: [oving5/card/CardPredicateTest.java](../../src/test/java/oving5/card/CardPredicateTest.java).
Hvordan en kan opprette funksjonelle grensesnitt kan du se på [wikisiden](https://www.ntnu.no/wiki/display/tdt4100/Lambda-uttrykk+og+funksjonelle+grensesnitt+i+Java+8) om dette.
+38
View File
@@ -0,0 +1,38 @@
# Interface - Named-oppgave
Denne oppgaven handler om et grensnitt (interface) for person-navn og hvordan slike implementeres og sorteres med grensesnittet `Comparator`.
I denne oppgaven tar vi utgangspunkt i at en person har et navn (`String`) bestående av fornavn ("given name") og etternavn ("family name") som sammen gir personens fulle navn ("full name").
Filene i denne oppgaven skal ligge i [`oving5/named`](../../src/main/java/oving5/named).
## Del 1
Definer et grensesnitt `Named` med følgende metoder:
- `void setGivenName(String)` og `String getGivenName()` for å sette og hente fornavn.
- `void setFamilyName(String)` og `String getFamilyName()` for å sette og hente etternavn.
- `void setFullName(String)` og `String getFullName()` for å sette og hente personens hele navn. Argumentet til set-metoden skal være fornavn og etternavn skilt med mellomrom. Tilsvarende skal get-metoden returnere fornavn og etternavn skilt med mellomrom.
## Del 2
Lag klassene `Person1` og `Person2` som begge implementerer grensesnittet `Named`. `Person1`-klassen skal ha felter for for- og etternavn (altså `givenName` og `familyName`) og en konstruktør som tar inn to tilsvarende argumenter. `Person2` skal ha ett felt for fullt navn (`fullName`) og en konstruktør som tar inn det fulle navnet. Begge skal imidlertid implementere samme logikk, dvs. ha get- og set-metoder for fornavn, etternavn og fullt navn. Man kan anta at brukeren oppretter `Person1` og `Person2`-objekter med gyldige navn, altså trenger man ikke å implementere valideringsmetoder.
## Del 3
For å kunne sammenligne `Named`-objekter, f.eks. for å sortere en kontaktliste, kan du lage en klasse kalt `NamedComparator`, som implementerer grensesnittet [`Comparator`](https://www.ntnu.no/wiki/display/tdt4100/Sortering+med+Comparable+og+Comparator). `NamedComparator`-objektet skal brukes for å sammenligne navn parvis: Først på etternavn og deretter på fornavn om etternavnene er like. Dette kan gjøres ved å la `NamedComparator`-klassen implementere metoden `int compare(Named named1, Named named2)` med følgende logikk:
- Dersom etternavnene er ulike skal metoden:
- returnere et negativt heltall om det første etternavnet er alfabetisk ordnet før det andre,
- eller et positivt heltall i motsatt tilfelle.
- Dersom etternavnene er like skal metoden gjøre det samme på fornavnene. Dersom også fornavnene er like skal metoden returnere 0.
Skriv testkode som bruker `Collections.sort`-metoden på en `ArrayList<Named>`, for å teste om `NamedComparator`-klassen har implementert [`Comparator`](https://www.ntnu.no/wiki/display/tdt4100/Sortering+med+Comparable+og+Comparator) riktig.
[Hint om sammenligning av strenger](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/String.html#compareTo(java.lang.String)).
Testkode for oppgavene finner du her:
- [oving5/named/NamedComparatorTest.java](../../src/test/java/oving5/named/NamedComparatorTest.java),
- [oving5/named/Person1Test.java](../../src/test/java/oving5/named/Person1Test.java),
- [oving5/named/Person2Test.java](../../src/test/java/oving5/named/Person2Test.java).
+49
View File
@@ -0,0 +1,49 @@
# Øving 5: Grensesnitt
## Øvingsmål
- Lære hva grensesnitt er og hvordan disse defineres
- Lære hvordan man implementerer et grensesnitt
- Kjenne til grunnleggende funksjonelle grensesnitt
## Øvingskrav
- Kunne lage grensesnitt og implementere disse med andre klasser
- Kunne bruke grensesnittene `Comparable<T>` og `Comparator<T>`
- Kunne bruke grensesnittene `Iterable<T>` og `Iterator<T>`
## Dette må du gjøre
### Del 1: Programmering
Gjør **minst to** av oppgavene under, for å få 2 poeng må det gjøres **minst fire** av oppgavene under, og minst en av dem må være markert som vanskelig. Oppgavene skal lagres i [`src/main/java/oving5`](../../src/main/java/oving5).
- [TicketControl](./TicketControl.md) (Lett)
- [CardContainer](./CardContainer.md) (Lett)
- [CardPredicate](./CardPredicate.md) (Lett)
- [CardComparison](./CardComparison.md) (Medium)
- [Twitter](./Twitter.md) (Medium)
- [Named](./Named.md) (Medium)
- [BinaryComputingIterator](./BinaryComputingIterator.md) (Vanskelig)
- [StringGrid](./StringGrid.md) (Vanskelig)
- [RPNKalkulator med funksjonelle grensesnitt](./RPNCalc.md) (Vanskelig)
Alle oppgavene er høyst eksamensrelevante. Vi har imidlertid valgt å trappe ned kravene for `Iterator`, så spesielt `StringGrid` går dypere i temaet enn dere kan forvente å se på eksamen.
### Del 2: Debugging
Gjør følgende oppgave om debugging og vis frem løsningen til studass på sal:
- [StringMergingIterator](./StringMergingIterator.md)
### Del 3: Sekvensdiagram
Lag et [sekvensdiagram](https://www.ntnu.no/wiki/display/tdt4100/Sekvensdiagrammer) som viser samhandlingen mellom et `StringMergingIterator`-objekt og dens argumenter. Dvs. lag et [sekvensdiagram](https://www.ntnu.no/wiki/display/tdt4100/Sekvensdiagrammer) som viser hvordan `StringMergingIterator` gjennom metodekall fletter verdiene fra de to gitte iteratorene (som blir tatt inn som argumentene til `StringMergingIterator`-objektet). Du trenger ikke å levere inn diagrammet på Blackboard.
### 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.
+25
View File
@@ -0,0 +1,25 @@
# Interface - RPNKalkulator med funksjonelle grensesnitt
Denne oppgaven handler om å gjøre det enklere å utvide en kalkulator ved å bruke det innebygde funksjonelle grensesnittet `BinaryOperator<T>`, sammen med `RPNCalc`-klassen.
Vi tar i denne oppgaven utgangspunkt i `RPNCalc`-klassen fra [Øving 3 - RPN-kalkulator](../oving3/RPNCalc.md)-oppgave, men det er ikke strengt nødvendig å ha gjort denne på forhånd. Et `RPNCalc`-objekt består av en stack med tall, metoder for å håndtere stacken, og `performOperation(char)` metoden, som utfører en operasjon gitt av parameteret. For eksempel vil kallet `performOperation('*')` fjerne de to øverste tallene fra stacken, multiplisere dem, og pushe resultatet på toppen av stacken. Om operasjonene hardkodes i metoden, vil det være vanskelig å endre hva kalkulatoren kan gjøre under kjøring. Denne oppgaven går derfor ut på å gjøre det mulig å legge til og fjerne operasjoner fra kalkulatoren. Operasjoner på en og to flyttall kan representeres ved bruk av henholdsvis `UnaryOperator<Double>`- og `BinaryOperator<Double>`-grensesnittene, og operasjoner uten operander (f.eks. pi) kan representeres ved bruk av `Supplier<Double>`.
Implementer følgende metoder i `RPNCalc`-klassen:
- `boolean addOperator(char, BinaryOperator<Double>)` - legger til en operator som virker på to tall (f.eks `+`) hvis operatoren ikke allerede er lagt til. Returner `true` hvis den nye operatoren blir lagt til. Operatoren kan ikke være `null`.
- `void removeOperator(char operator)` - fjerner operatoren med tegn `operator`.
Du må også oppdatere `performOperation(char)` til å bruke operatorene som legges til via metodene over. Om man prøver å kalle `performOperation` med en operator som ikke er lagt til skal det utløses et `UnsupportedOperationException`-unntak.
## Valgfri Ekstraoppgave
Utvid programmet til å kunne ta inn operatorer som tar inn et parameter (`UnaryOperator<Double>`, f.eks. `|` (absoluttverdi)) og ingen parametre (`Supplier<Double>`, f.eks. `p` (pi)). Husk at du må håndtere forsøk på å legge til samme operator i flere kategorier (f.eks. om man prøver å legge til `+` som både `UnaryOperator` og `BinaryOperator` må det håndteres på en god måte).
Dette vil innebære å legge til metodene:
- `boolean addOperator(char, UnaryOperator<Double>)`
- `boolean addOperator(char, Supplier<Double>)`
som fungerer på samme måte som `addOperator(char, BinaryOperator<Double>)`, samt å oppdatere resten av koden til å fungere med de forskjellige operatortypene.
Testkode finner du her: [oving5/RPNCalcTest.java](../../src/test/java/oving5/RPNCalcTest.java).
+73
View File
@@ -0,0 +1,73 @@
# Interface - StringGrid-oppgave
Denne oppgaven handler om et grensnitt (interface) for rutenett som holder strenger (StringGrid), hvordan slike implementeres og hvordan en kan iterere gjennom et slikt rutenett ved hjelp av en [`Iterator`](https://www.ntnu.no/wiki/display/tdt4100/Iterasjon+med+Iterator+og+Iterable).
I denne oppgaven tar vi utgangspunkt i et `StringGrid`-grensesnitt som definerer metodene til et rutenett som holder strenger. Et rutenett er firkantet og består av et antall rader og kolonner. Det skal være mulig å spørre rutenettet hvilken streng som er på angitt rad og kolonne i tillegg til å endre strengen på angitt rad og kolonne. Denne oppførselen er oppsummert i det definerte `StringGrid`-grensesnittet under:
Filene i denne oppgaven skal ligge i [`oving5/stringgrid`](../../src/main/java/oving5/stringgrid).
```java
package oving5.stringgrid;
/**
* An interface with methods for managing the content of a String grid. The grid has a number of
* rows (the grid's height) and columns (the grid's width). In each cell in the grid there is a
* String that can be set with the setElement method and read with the getElement method.
*/
public interface StringGrid {
// Returns the number of rows in this StringGrid
int getRowCount();
// Returns the number of columns in this StringGrid
int getColumnCount();
// Returns the String at the given row and column. Throws an IllegalArgumentException if the
// row or column is out of range
String getElement(int row, int column);
// Sets the String at the given row and column. Throws an IllegalArgumentException if the row
// or column is out of range
void setElement(int row, int column, String element);
}
```
Alle klasser som implementerer **StringGrid**-grensesnittet må støtte de fire definerte metodene.
## Del 1 - StringGrid-grensesnitt og implementerende StringGridImpl-klasse
Lag en `StringGridImpl`-klasse som implementerer `StringGrid`-grensesnittet definert over. Merk at grensesnitt ikke kan brukes til å spesifisere konstruktører, så du må selv definere en eller flere egnede konstruktører. Det er imidlertid nødvendig å implementere en konstruktør som tilsvarer den JUnit-testen forventer:
- `StringGridImpl(int rows, int columnCount)` - konstruktør som tar inn antall rader som `rows` og antall kolonner som `columnCount`.
Du står fritt til å velge hvordan metodene definert av grensesnittet skal implementeres så lenge de tilfredsstiller den definerte oppførselen.
**Hint:** Bruk en enkel `ArrayList<String>` eller en dobbel `ArrayList<ArrayList<String>>` (se wiki-siden om [todimensjonale matriser](https://www.ntnu.no/wiki/display/tdt4100/Todimensjonale+matriser)). Du kan også bruke en enkel `String[]` eller en todimensjonal `String[][]` for å lagre strengene. Implementasjonen er helt opp til deg, så lenge den oppfører seg som beskrevet i grensesnittet.
## Del 2 - StringGridIterator-klasse
Det er hensiktmessig å kunne iterere over alle elementene i et rutenett som implementerer grensesnittet `StringGrid`, f.eks. når en bygger en streng i en `toString()`-metode eller skal sjekke om et spill har blitt avsluttet/vunnet. I denne deloppgaven skal du lage en slik [`Iterator`](https://www.ntnu.no/wiki/display/tdt4100/Iterasjon+med+Iterator+og+Iterable)-implementasjon, kalt `StringGridIterator`. Denne klassen må implementere grensesnittet `Iterator<String>`, siden `StringGrid` inneholder `String`-objekter. I tillegg til metodene som er definert i `Iterator`-grensesnittet, må `StringGridIterator` ha en konstruktør som tar imot hvilken `StringGrid` det skal itereres over og i hvilken rekkefølge elementene skal returneres i. Disse verdiene må huskes, så koden i `Iterator`-metodene kan brukes dem til å styre iterasjonen. `StringGridIterator`-klassen må altså støtte følgende konstruktør/metoder:
- `StringGridIterator(StringGrid, boolean rowMajor)` - konstruktør som tar inn `StringGrid`-objektet som `StringGridIterator`-klassen skal iterere over i tillegg til en logisk verdi som angir om iterasjonen skal være bortover først (`rowMajor` er `true`) eller nedover først (`rowMajor` er `false`).
- `boolean hasNext()` - returnerer `true` så lenge det er flere `String`-objekter igjen i `StringGrid`-objektet som ikke ennå er blitt iterert over (med andre ord, sjekk om du har kommet til siste rute i rutenettet).
- `String next()` - returnerer det neste `String`-objektet i rutenettet. Hvilken `String` som er den neste, avhenger av hvordan rutenettet skal itereres (med andre ord, hvorvidt om `rowMajor` er `true` eller `false`).
- `void remove()` - denne metoden skal bare utløse et unntak av typen `UnsupportedOperationException` siden det ikke skal være mulig å fjerne `String`-objekter fra rutenettet.
## Del 3 - Iterable-grensesnittet
Endre `StringGrid`-grensesnittet slik at det utvider (med `extends`) [`Iterable<String>`](https://www.ntnu.no/wiki/display/tdt4100/Iterasjon+med+Iterator+og+Iterable). Dette skal gjøre det mulig å skrive for-setningen under, for å gå gjennom alle elementene i rutenettet.
```java
StringGrid stringGrid = ... // her initialiseres stringGrid
// gå gjennom alle elementene i stringGrid
for (String s : stringGrid) {
// gjør noe med s her
}
```
Rekkefølgen som en slik `for`-løkke går gjennom elementene på, skal være hele første rad, så hele andre rad osv. til og med siste rad.
Hva slags følger får det for `StringGridImpl`? Gjør nødvendige endringer i den også, og test at det virker!
Testkode for del 1 og del 2 finner du her: [oving5/stringgrid/StringGridTest.java](../../src/test/java/oving5/stringgrid/StringGridTest.java).
@@ -0,0 +1,22 @@
# Debugging - StringMergingIterator-oppgave
Oppgaven handler om feilsøking ("debugging") av en Iterator-implementasjon ved bruk av [**debuggeren**](https://www.ntnu.no/wiki/pages/viewpage.action?pageId=235996724) i VS Code.
Les først denne artikkelen om bruk av debugger i VS Code om du ikke er kjent med dette: [Kjøring av kode og debugging i VS Code](https://www.ntnu.no/wiki/pages/viewpage.action?pageId=235996724)
Klassen `StringMergingIterator` implementerer grensesnittet [`Iterator<String>`](https://www.ntnu.no/wiki/display/tdt4100/Iterasjon+med+Iterator+og+Iterable), og leverer annenhver verdi fra to andre iteratorer av typen `Iterator<String>`. Denne iteratoren fletter altså verdiene fra to andre gitte iteratorer, og er altså en meta-iterator. Meta-iteratorer er iteratorer som bruker andre iteratorer som datakilder.
`StringMergingIterator` har følgende konstruktør:
- `StringMergingIterator(Iterator<String> first, Iterator<String> second)`
Siden klassen implementerer `Iterator<String>` har den også følgende metoder:
- `boolean hasNext()` - returnerer `true` dersom iteratoren har flere verdier, `false` dersom det ikke er flere verdier.
- `String next()` - returnerer den neste verdien fra iteratoren, eller utløser et `NoSuchElementException` dersom iteratoren er tom.
I denne oppgaven blir en implementasjon av `StringMergingIterator` sammen med et testprogram utdelt, men i implementasjonen av klassen har vi plantet en eller flere feil. Målet for oppgaven er å finne feilene i implementasjonen ved hjelp av [debuggeren](https://www.ntnu.no/wiki/pages/viewpage.action?pageId=235996724) i VS Code. Kjør programklassen `StringMergingIteratorProgram` i debug-modus, og bruk dette til å finne ut hvor `StringMergeIterator` gjør feil. Dersom programklassen lykkes med å få en flettet strøm med verdier har du funnet alle feilene.
Merk at du *ikke* skal gjøre noen endringer `StringMergingIteratorProgram`, men bruke dette programmet til å teste logikken i `StringMergingIterator`.
Programmet du skal feilsøke er `StringMergingIteratorProgram`, og du finner koden for denne i [oving5/debugging/StringMergingIteratorProgram.java](../../src/main/java/oving5/debugging/StringMergingIteratorProgram.java). Koden for `StringMergingIterator` finner du i [oving5/debugging/StringMergingIterator.java](../../src/main/java/oving5/debugging/StringMergingIterator.java).
+27
View File
@@ -0,0 +1,27 @@
# Interface - TicketControl-oppgave
Denne oppgaven handler om å lage en representasjon av billetter. Du skal lage et grensesnitt for billetter, og to klasser som implementer dette; en som representerer en enkeltbillett og en som representerer en periodebillett.
Filene i denne oppgaven skal ligge i [`oving5/ticket`](../../src/main/java/oving5/ticket).
## Del 1 - Ticket-grensesnitt
Lag et grensesnitt kalt `Ticket`, med følgende metoder:
- `boolean scan()` - Denne metoden kalles hver gang billetten scannes. Dersom billetten er gyldig, skal metoden returnere `true`, ellers `false`. Legg merke til at du **ikke** skal implementere metoden her, du skal bare definere grensesnittet.
Tanken her er at vi kan senere lage en kontrollørklasse som kan bruke `scan()`-metoden for å sjekke om en billett er gyldig.
## Del 2 - SingleTicket-klasse
Lag en klasse kalt `SingleTicket` som implementerer `Ticket`-grensesnittet. `SingleTicket` skal implementere `scan()`-metoden slik at den kun returnerer `true` den første gangen metoden blir kalt.
**Hint:** Du kan bruke en `boolean`-variabel for å holde styr på om billetten er gyldig eller ikke.
Testkode for oppgaven finner du her: [oving5/ticket/SingleTicketTest.java](../../src/test/java/oving5/ticket/SingleTicketTest.java).
## Del 3 - PeriodTicket-klasse
Lag en klasse kalt `PeriodTicket` som implementerer `Ticket`-grensesnittet. `PeriodTicket` skal implementere `scan()`-metoden slik at den returnerer `true` dersom det nåværende tidspunktet er mellom to gitte tidspunkter. `PeriodTicket` skal ha en konstruktør som tar inn to `LocalDateTime`-objekter som representerer start- og sluttidspunktet for gyldigheten til billetten. Legg til validering der det gir mening.
Testkode for oppgaven finner du her: [oving5/ticket/PeriodTicketTest.java](../../src/test/java/oving5/ticket/PeriodTicketTest.java).
+55
View File
@@ -0,0 +1,55 @@
# Interface - Sortering av TwitterAccount-objekter ved bruk av Comparator
Denne oppgaven handler om sortering av `TwitterAccount`-objekter, ved bruk av grensesnittet `Comparator`. Oppgaven illustrerer hvordan man kan sortere objekter av samme klasse på ulike måter, ved hjelp av ulike implementasjoner av `Comparator`.
Vi tar i denne oppgaven utgangspunkt i `TwitterAccount`- og `Tweet`-klassen fra [Øving 4 - Twitter-oppgave](../oving4/Twitter.md). Et `TwitterAccount`-objekt har et brukernavn, en liste over andre brukere som følges, en liste over brukere som følger denne brukeren (dette `TwitterAccount`-objektet), og en liste med tweets. Vi ønsker å kunne sortere `TwitterAccount`-objekter på tre ulike parametre:
1. Brukernavn
2. Antall følgere
3. Antall tweet
Dersom du ikke har gjort `Twitter`-oppgaven allerede, bør du gjøre denne først. Løsningsforslaget kommer til å være tilgjengelig [her](https://git.ntnu.no/tdt4100/tdt4100-lf-25/blob/main/src/main/java/oving4/twitter) etter siste demonstrasjonsfrist for øving 4.
Filene i denne oppgaven skal ligge i [`oving5/twitter`](../../src/main/java/oving5/twitter).
## Del 1
I denne delen av oppgaven skal du lage tre ulike implementasjoner av `Comparator`-grensesnittet. `Comparator`-grensesnittet inneholder én metode: `int compare(Object o1, Object o2)`. Implementasjonen av denne metoden skal returnere:
- Et negativt tall dersom objektet o1 skal komme før objektet o2 i en sortert rekkefølge.
- Et positivt tall dersom objektet o1 skal komme etter objektet o2 i en sortert rekkefølge.
- $0$ om det er likegyldig hvilken rekkefølge objektene har (dvs. de er like hverandre for den parameteren/de paremetrene de sorteres på).
De tre klassene du skal lage er som følger:
- `UserNameComparator`: Sammenligner `TwitterAccount`-objektene på brukernavn, slik at brukeren “Apekatten” vil komme før “Bjørnen” som igjen vil komme før “Cameleonen” (dvs. leksikalsk rekkefølge - tenk rekkefølgene brukernavnene ville stått i et leksikon eller en ordbok).
- `FollowersCountComparator`: Sammenligner `TwitterAccount`-objektene på antall følgere, slik at brukeren med flest følgere havner først.
- `TweetsCountComparator`: Sammenligner `TwitterAccount`-objektene på antall tweets, slik at brukeren med flest tweets havner først.
Alle klassene skal implementere `Comparator<TwitterAccount>`.
[Hint om sammenligning av strenger](<https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/String.html#compareTo(java.lang.String)>).
[Hint om sammenligning av tall](<https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Integer.html#compareTo(java.lang.Integer)>).
## Del 2
I denne delen av oppgaven skal du legge til en funksjon i `TwitterAccount`-klassen som lar deg hente ut en sortert versjon av følgerene til dette (`this`) `TwitterAccount`-objektet. Funksjonen du skal implementere er som følger:
- `List<TwitterAccount> getFollowers(Comparator<TwitterAccount>)` - skal returnere en sortert kopi av følgere-listen til dette `TwitterAccount`-objektet. Objektene skal sorteres ved å bruke det `Comparator`-objektet som tas inn som parameter. Dersom parameteren er `null` skal du returnere den orginale (usorterte) versjonen av følgere-listen. Du skal ikke skrive din egen sorteringsalgoritme, men bruke `Collections.sort`-funksjonen fra `java.utils`-biblioteket. Merk at den opprinnelige følgere-listen skal være uforandret etter at denne funksjonen har blitt kjørt.
Testkode for oppgaven finner du her:
- [oving5/twitter/TwitterAccountTest.java](../../src/test/java/oving5/twitter/TwitterAccountTest.java)
- [oving5/twitter/FollowersCountComparatorTest.java](../../src/test/java/oving5/twitter/FollowersCountComparatorTest.java)
- [oving5/twitter/TweetsCountComparatorTest.java](../../src/test/java/oving5/twitter/TweetsCountComparatorTest.java)
- [oving5/twitter/UserNameComparatorTest.java](../../src/test/java/oving5/twitter/UserNameComparatorTest.java)
## Valgfri Ekstraoppgave
Lag en klasse `TwitterAccountComparator` som implementerer `Comparator<TwitterAccount>` og sammenligner `TwitterAccount`-objekter på
følgende måte:
- `TwitterAccount`-objektet med flest følgere skal komme først.
- Dersom to `TwitterAccount`-objekter har like mange følgere skal det `TwitterAccount`-objektet med flest tweets komme først.
- Dersom to `TwitterAccount`-objekter har like mange følgere og tweets skal `TwitterAccount`-objektene sammenlignes på brukernavn.
+46
View File
@@ -0,0 +1,46 @@
# Observatør-observert-teknikken - HighscoreList-oppgave
Denne oppgaven handler om å bruke observatør-observert-teknikken for å bli informert om endringer i en highscore-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. Dette brukes gjerne når vi har en rekke observatørobjekter som ønsker å vite når en endring skjer i et annet (observert) objekt. Det hadde vært ueffektivt om observatørobjektene skulle sjekket for endringer hele tiden. Derfor definerer vi ofte et felles `interface` som disse kan implementere, slik at det observerte objektet kan kalle på metoder i observatørene når det skjer en endring.
I denne oppgaven skal vi lage en observerbar `HighscoreList` som kan si fra til observatører/lyttere av typen `HighscoreListListener` 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 konsollen som legges til listen og skriver ut listen hver gang et nytt resultat faktisk blir lagt til.
Alle filene i denne oppgaven skal lages i [oving6/highscorelist](../../src/main/java/oving6/highscorelist).
## Del 1: Implementasjon av HighscoreList
En `HighscoreList` skal holde styr på heltallsresultater (av typen `int`/`Integer`). Listen skal være observerbar ved at den kan registrere lyttere (`HighscoreListListener`-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 HighscoreList(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.
`HighscoreListListener`-grensesnittet er vist i klassediagrammet til venstre og må implementeres av alle klasser som ønsker å fungere som lyttere for `HighscoreList`-instanser. Lyttere registrerer seg med `HighscoreList` sin `addHighscoreListListener`-metode og vil siden få beskjed om nye resultater ved at `listChanged()`-metoden kalles. Argumentene som tas inn er `HighscoreList`-objektet som ble endret og posisjonen i listen der endringen skjedde.
Her er en oversikt over metoden som må implementeres:
- `HighscoreList(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 på hvilken posisjon som ble endret.
- `void addHighscoreListListener(HighscoreListListener)` - registrerer en ny lytter.
- `void removeHighscoreListListener(HighscoreListListener)` - fjerner en tidligere registrert lytter.
Klassediagram for `HighscoreList` og `HighscoreListListener`:
![highscore-list](./img/highscore-list.png)
Testkode for denne oppgaven finner du her: [oving6/highscorelist/HighscoreListTest.java](../../src/test/java/oving6/highscorelist/HighscoreListTest.java).
## Del 2: Hovedprogramklasse
Lag en hovedprogramklasse kalt `HighscoreListProgram`, som tester at `HighscoreList`-klassen din virker som den skal. La den opprette en `HighscoreList`-instans, lese inn tall fra konsollet (f.eks. med en `Scanner` og `nextInt`-metoden) og legge disse inn i listen. Sørg for at `HighscoreListProgram` implementerer `HighscoreListListener`-grensesnittet (`HighscoreListProgram implements HighscoreListListener`) og registrerer seg som lytter på `HighscoreList`-instansen via `addHighscoreListListener`. 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 `HighscoreList` og registrerer seg selv (altså `HighscoreListProgram`-instansen) som lytter. Dette kan og gjøres i konstruktøren om ønskelig.
- `void run()` - leser inn tall (resultater) fra terminalen og legger dem til i listen.
- `void listChanged(HighscoreList, int)` (fra `HighscoreListListener`) - observerer endringer i `HighscoreList`-instansen og skriver ut posisjonsargumentet, samt selve listen, til konsollen.
Klassediagrammet viser hvordan klassene henger sammen, og vårt forslag til metoder:
![hl-program](./img/hl-program.png)
Husk også å lage en `main()`-metode som kjører HighscoreListProgram!
+136
View File
@@ -0,0 +1,136 @@
# Delegering - Logger-oppgave
Denne oppgaven bruker delegeringsteknikken for å implementere en fleksibel måte å håndtere logging (av feil i programmer) på.
## Logging
Ved kjøring av programmer er det ofte behov for å logge hva som skjer underveis, slik at det blir lettere å drive feilsøking i etterkant. F.eks. kan en lagre feilmeldinger til fil, med tidspunkt og litt om programtilstanden og hvis programmet kræsjer ordentlig, så kan brukeren sende logg-fila som e-post til utviklerne. En enkel måte å støtte logging på er å lage en hjelpeklasse med én metode, f.eks. `log(String melding)`, og så er det hjelpeklassen som bestemmer om meldingen skal vises i statuslinja, skrives til fil, sendes som melding til en alarmsentral osv. Hjelpeklassen kan kanskje brukes av mange programmer, og siden behovene vil variere er det viktig å gjøre dette fleksibelt. Denne oppgaven bruker [grensesnitt](https://www.ntnu.no/wiki/pages/viewpage.action?pageId=65936813) og [delegeringsteknikken](https://www.ntnu.no/wiki/display/tdt4100/Delegeringsteknikken) for å implementere fleksibel logging, litt på samme måte som eksisterende loggingsrammeverk (se f.eks. [Java sin egen loggingsfunksjonalitet](http://docs.oracle.com/javase/6/docs/technotes/guides/logging/overview.html), Apache sitt [log4j-rammeverk](http://logging.apache.org/log4j/), eller Googles ["Java logging framework"](https://www.google.no/search?q=java+logging+frameworks)).
Alle filene i denne oppgaven skal lages i [oving6/logger](../../src/main/java/oving6/logger).
### ILogger-grensesnittet
Logging gjøres ved å bruke ulike implementasjoner av `ILogger`, som er definert som følger:
```java
package oving6.logger;
public interface ILogger {
String ERROR = "error";
String INFO = "info";
String WARNING = "warning";
void log(String severity, String message, Exception exception);
}
```
ILogger-grensesnittet definerer én `log`-metode som brukes til all logging:
- `severity`-argumentet angir alvorlighetsgraden, og må være en av `String`-verdiene `ERROR`, `WARNING` eller `INFO`, som er definert som konstanter i grensesnittet.
- `message`-argumentet er en melding om hva som var feil.
- `exception`-argumentet er et unntaksobjekt, som kan gi mer informasjon av hva som var feil, men kan være `null`.
En typisk bruk vil være i `catch`-delen av en `try/catch`:
```java
ILogger logger = ...
...
try {
...
} catch (IOException ioe) {
logger.log(ILogger.ERROR, "Feil ved lesing fra fil", ioe);
}
```
Akkurat hvordan logging utføres bestemmes av hvilken implementasjon av ILogger-grensesnittet en bruker, og i denne oppgaven skal du implementere følgende tre klasser:
- `DistributingLogger` - delegerer til andre loggere basert på _alvorlighetsgraden_.
- `FilteringLogger` - delegerer til en annen logger, men kun for spesifikke alvorlighetsgrader.
- `StreamLogger` - skriver logg-meldingen til en angitt strøm.
Hver av disse utgjør én av deloppgavene beskrevet under.
## Del 1 - StreamLogger
En `StreamLogger` sørger for å skrive alle logg-meldinger til en angitt `OutputStream`, med én melding pr. linje (altså linjeskift mellom hver melding). `OutputStream`-objektet må gis inn i konstruktøren:
- `StreamLogger(OutputStream stream)` - initialiserer `StreamLogger`-objektet slik at logg-meldinger skrives til `stream`.
Eksempel på bruk:
```java
ILogger logger = new StreamLogger(System.out);
logger.log(ILogger.INFO, "Denne meldingen er til informasjon og skrives til System.out", null);
```
Husk å kalle `flush`-metoden til OutputStream etter at logg-meldingen er skrevet.
Det skal også være mulig å angi en såkalt _format_-string, dvs. en `String` som fungerer som en slags mal for hva som skrives, f.eks. `"%s: %s (%s)"`:
- `void setFormatString(String formatString)` - setter format-string-en som brukes for å lage logg-meldingen som skrives. Det settes ingen andre krav til validering av `formatString`-argumentet annet enn at det ikke kan være `null`.
Effekten av skriving skal være som om man ga format-string-en som første argument til `String.format`-metoden etterfulgt av `severity`-, `message`- og `exception`-argumentene, og så skrev ut det denne metoden returnerer:
```java
String logMessage = String.format(formatString, severity, message, exception);
// skriv logMessage til OutputStream-en her
```
Merk at dersom format-string-en ikke er satt, så skal den ha en fornuftig start-verdi.
Testkode for oppgaven: [oving6/logger/StreamLoggerTest.java](../../src/test/java/oving6/logger/StreamLoggerTest.java).
## Del 2 - FilteringLogger
`FilteringLogger`-klassen implementerer `ILogger`-grensesnittet og delegerer til en annen `ILogger`-implementasjon, men bare hvis _alvorlighetsgraden_ er en av et sett angitte verdier. Både loggeren det delegeres til og alvorlighetsgradene angis når `FilteringLogger`-objektet opprettes:
- `FilteringLogger(ILogger logger, String... severities)` - initialiserer `FilteringLogger`-objektet så det delegerer logging til `logger`-argumentet, men bare hvis _alvorlighetsgraden_ som gis til `log`-metoden er en av verdiene angitt i `severities`-argumentet. `severities`-argumentet er et såkalt varargs-argument, som du kan lese mer om her: [Varargs - variabelt antall argumenter](https://www.ntnu.no/wiki/display/tdt4100/Varargs+-+variabelt+antall+argumenter). Det viktigste å vite her er at det du får inn i metoden din vil være en variabel `severities` som er av typen string array (`String[]`). Du kan hente ut elementer her via `severities[0]`, sjekke lengde ved `severities.length` og ellers bruke alle normale arraymetoder.
Det skal også være mulig å sjekke om logging er på og slå logging av og på i etterkant:
- `boolean isLogging(String severity)` - returnerer `true` hvis logging er slått på for den angitte alvorlighetsgraden og `false` ellers.
- `void setIsLogging(String severity, boolean value)` - slår logging på (`value` er `true`) eller av (`value` er `false`) for den angitte _alvorlighetsgraden_.
Eksempel på bruk:
```java
ILogger syserrLogger = new StreamLogger(System.err);
FilteringLogger logger = new FilteringLogger(syserrLogger, ILogger.ERROR);
logger.log(ILogger.ERROR, "Denne meldingen er alvorlig og skrives til System.err", null);
logger.log(ILogger.WARNING, "Denne meldingen er en advarsel og blir filtrert bort", null);
logger.log(ILogger.INFO, "Denne meldingen er til informasjon og blir filtrert bort", null);
logger.setIsLogging(ILogger.WARNING, true);
logger.log(ILogger.WARNING, "Denne meldingen er en advarsel og blir nå skrevet til System.err", null);
```
Testkode for oppgaven: [oving6/logger/FilteringLoggerTest.java](../../src/test/java/oving6/logger/FilteringLoggerTest.java).
## Del 3 - DistributingLogger
`DistributingLogger`-klassen brukes for å fordele logg-meldinger til en av tre andre loggere, avhengig av _alvorlighetsgraden_ til en logg-melding. Den har én hjelpe-logger for meldinger med alvorlighetsgrad `ERROR`, én for meldinger av alvorlighetsgrad `WARNING` og en for meldinger av alvorlighetsgrad `INFO`. Alle disse angis til konstruktøren:
- `DistributingLogger(ILogger errorLogger, ILogger warningLogger, ILogger infoLogger)` - initialiserer objektet slik at den første loggeren brukes til alvorlighetsgraden `ERROR`, den andre til alvorlighetsgraden `WARNING` og den tredje til alvorlighetsgraden `INFO`.
I tillegg skal klassen ha en metode for å sette hver av dem individuelt:
- `void setLogger(String severity, ILogger logger)` - setter/endrer loggeren som brukes for den angitte alvorlighetsgraden.
Eksempel på bruk:
```java
ILogger syserrLogger = new StreamLogger(System.err);
ILogger sysoutLogger = new StreamLogger(System.out);
DistributingLogger logger = new DistributingLogger(syserrLogger, syserrLogger, sysoutLogger);
logger.log(ILogger.ERROR, "Denne meldingen er alvorlig og skrives til System.err", null);
logger.log(ILogger.WARNING, "Denne meldingen er en advarsel og skrives til System.err", null);
logger.log(ILogger.INFO, "Denne meldingen er til informasjon og skrives til System.out", null);
logger.setLogger(ILogger.WARNING, sysoutLogger);
logger.log(ILogger.WARNING, "Denne meldingen er en advarsel, men nå skrives den til System.out", null);
```
Testkode for oppgaven: [oving6/logger/DistributingLoggerTest.java](../../src/test/java/oving6/logger/DistributingLoggerTest.java).
+54
View File
@@ -0,0 +1,54 @@
# Delegering - The Office-oppgave
Denne oppgaven bruker delegeringsteknikken for å modellere arbeidsfordeling på en “vanlig” arbeidsplass. Denne oppgaven kan muligens oppleves som mindre meningsfull. Dette er kanskje omtrent tilsvarende hvor meningsløst noen typer kontorarbeid kan virke.
Vi skal i dette scenarioet ha en sjef, eller `Manager`, som har én eller flere arbeidere, eller `Clerk`s, altså i en såkalt én-til-mange relasjon. Et `Employee`-grensesnitt definerer en oppførsel som er felles for de ansatte, og implementeres av både `Manager` og `Clerk`.
`Employee`-objekter på denne simulerte arbeidsplassen har to oppgaver:
- Utskrift av dokumenter
- Utførelse av matematiske beregninger
Alle filene i denne oppgaven skal lages i [oving6/office](../../src/main/java/oving6/office).
## Del 1: Employee, Clerk og Printer
`Employee`-grensesnittet har følgende metoder:
- `double doCalculations(BinaryOperator<Double> operation, double value1, double value2)` - regner ut resultatet av å utføre `operation` med argumentene `value1` og `value2`.
- `void printDocument(String document)` - Printer `document`. Hvordan dette gjøres avhenger av den spesifikke implementasjonen.
- `int getTaskCount()` - returnerer hvor mange oppgaver (beregninger og printinger) som har blitt utført av eller på vegne av dette `Employee`-objektet.
- `int getResourceCount()` - antallet employees til rådighet, inkludert `Employee`-objektet metoden blir kalt på. En `Employee` skal altså medregne seg selv i antall ressurser den ansatte har til rådighet. Dette tallet skal inkludere alle `Employee`-objekter nedover i hierarkiet.
Lag dette grensesnittet, og lag så en `Clerk`-klasse som implementerer det. `Clerk` må ha følgende konstruktør:
- `Clerk(Printer printer)`
`Clerk`-klassen må inneholde egen logikk for å løse `doCalculations`, men skal altså delegere `printDocuments` til `Printer`-objektet gitt i konstruktøren.
Definer en `Printer`-klasse med følgende metoder:
- `void printDocument(String document, Employee employee)` - skriver documentet til konsollen og tar vare på dokumentet i `employee` sin historikk. Ingen av argumentene kan være `null`.
- `List<String> getPrintHistory(Employee employee)` - returnerer en `List<String>` med alle dokumentene som har blitt printet av `employee` av denne printeren i rekkefølgen de har blitt printet. Om `employee` ikke har printet noen dokumenter ved denne printeren skal en tom liste returneres.
La så `Clerk` delegere `printDocument` til `Printer`. Siden `Clerk` ikke har noen andre ansatte å delegere til, vil `getResourceCount()` alltid være 1.
Testkode for `Clerk` er her: [oving6/office/ClerkTest.java](../../src/test/java/oving6/office/ClerkTest.java).
Testkode for `Printer` er her: [oving6/office/PrinterTest.java](../../src/test/java/oving6/office/PrinterTest.java).
## Del 2: Manager
Vi definerer så sjefen til de hardt-arbeidende `Clerk`-objektene. `Manager`-klassen har følgende konstruktør:
- `Manager (Collection<Employee> employees)` - utløser et `IllegalArgumentException` dersom employees er tom.
La så `Manager` implementere `Employee`-grensesnittet. Implementer `Manager`s oppgaver ved å delegere alle videre til en av arbeiderne i listen med `Employee`-objekter gitt i konstruktøren. Regelen for hvilken `Employee` som får hvilken oppgave delegert til seg kan du bestemme selv, men prøv å gjøre det slik at arbeidet fordeles jevnt på alle. Mens `Clerk` altså har kun én tilgjengelig ressurs vil `Manager`-objekter vil ha flere.
Testkode for `Manager` er her: [oving6/office/ManagerTest.java](../../src/test/java/oving6/office/ManagerTest.java).
## Del 3: Main-metode
Lag en `main()`-metode som illustrerer hva som skjer med effektiviteten når vi legger til flere nivåer med mellomledere.
Lag først et `Manager`-objekt som blir tildelt noen `Clerk`-objekter under seg. Presentér deretter effektiviteten av hierarkiet ved å skrive ut `getTaskCount() / getResourceCount()` for `Manager`-objektet. Vis deretter hvordan effektiviteten faller når vi legger til nivåer med mellomledere ved å lage to eller flere nivåer med `Manager`, hvor lederne på bunnen tildeles `Clerk`-objekter, og skriv ut den nye effektiviteten for topplederne.
+44
View File
@@ -0,0 +1,44 @@
# Øving 06: Observatør-Observert og Delegering
## Øvingsmål
- Lære hva observatør-observert-teknikken er, dens bruksområder og fordeler
- Lære bruk av delegering for å utføre oppgaver i en klasse
## Øvingskrav
- Kunne definere og implementere et observatørgrensesnitt
- Kunne la en observert klasse fortelle dens observatører om endringer
- Kunne la en klasse delegere utførelsen av oppgaver til interne objekter
## Dette må du gjøre
### Del 1: Programmering
Denne øvingen omfatter både [delegeringsteknikken](https://www.ntnu.no/wiki/display/tdt4100/Delegeringsteknikken) og [observatør-observert-teknikken](https://www.ntnu.no/wiki/pages/viewpage.action?pageId=66879660). Gjør **minst én** av de fire oppgavene under. For å få 2 poeng må det gjøres **minst én** oppgave fra **hvert av de to temaene**. Dette anbefales uansett på det *sterkeste*, siden dette må til for å dekke hele pensum.
Gjennomfør enten *minst én* av oppgavene om delegering:
- [The Office](./Office.md) (anbefalt) (Lett)
- [Logger](./Logger.md) (Medium)
ELLER *minst én* av oppgavene om observatør-observert-teknikken:
- [StockListener](./StockListener.md) (Medium)
- [Highscore](./HighscoreList.md) (Vanskelig)
**I tillegg til oppgaven(e) ovenfor skal du levere en tekstfil hvor du gjør kort rede for delegeringsteknikken og observatør-observert-teknikken.**
### Del 2: Objektdiagram
For en av oppgavene du gjorde i del 1:
Lag en sekvens av kall i `main()`-metoden. Denne sekvensen må benytte seg av den passende teknikken fra del 1. Tegn deretter et [objektdiagram](https://www.ntnu.no/wiki/display/tdt4100/Objektdiagrammer) som viser tilstanden til hvert objekt ved slutten av `main()`-metoden. Du trenger ikke levere inn diagrammet på Blackboard.
## Hjelp / mistanke om bugs
Ved spørsmål eller behov for hjelp konsulter studassen din i saltidene 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.
+64
View File
@@ -0,0 +1,64 @@
# Observatør-observert-teknikken - StockListener-oppgave
Denne oppgaven handler om å bruke observatør-observert-teknikken for å holde en aksjeindeks (`StockIndex`) informert om endringer i én eller flere aksjer (`Stock`).
Observatør-observert-teknikken går ut på at det observerte objektet sier ifra til én eller flere observatører om at tilstanden er endret. I vårt tilfelle skal vi ta utgangspunkt i at aksjer (`Stock`) har en pris, og at personer eller institusjoner (`StockListener`) ønsker å holde seg oppdatert på aksjepriser.
Alle filene i denne oppgaven skal lages i [oving6/stock](../../src/main/java/oving6/stock).
## Del 1: Stock-klassen og StockListener-grensesnittet
Du skal implementere en klasse `Stock` med følgende funksjonalitet:
- `Stock(String ticker, double price)` - en konstruktør som tar inn en aksjekode (`ticker` ulik `null`) og en aksjepris.
- `void setPrice(double price)` - endringsmetode for aksjeprisen. Dersom aksjepris er negativ eller lik null, skal metoden utløse en `IllegalArgumentException`.
- `String getTicker()` - metode for å hente aksjekoden.
- `double getPrice()` - metode for å hente aksjeprisen.
Du skal videre definere et lyttergrensesnitt kalt `StockListener`, som observatørene må implementere. Grensesnittet skal inneholde én metode:
- `void stockPriceChanged(Stock stock, double oldValue, double newValue)` - lyttermetode for å holde lytteren oppdatert på aksjeprisen. Metoden skal ta inn et `Stock`-objekt, samt gammel og ny pris. Alle lyttere må implementere denne metoden.
Foreløpig er `Stock` ikke observerbar. For at observatører skal kunne holdes oppdatert, må `Stock`-objekter administrere en liste med lyttere. Derfor må `Stock`-klassen i tillegg ha følgende metoder:
- `void addStockListener(StockListener)` - metode for å legge til nye observatører.
- `void removeStockListener(StockListener)` - metode for å fjerne observatører.
Observatørene skal holdes oppdatert på prisendringer. Derfor må lyttermetoden kalles hos alle registrerte observatører når aksjeprisen endres med `setPrice`-metoden.
Testkode for denne oppgaven finner du her: [oving6/stock/StockTest.java](../../src/test/java/oving6/stock/StockTest.java).
## Del 2: StockIndex implements StockListener
Vi skal nå lage en veldig forenklet versjon av en aksjeindeks. I korte trekk bruker man en aksjeindeks til å måle utviklingen av et utvalg aksjer. Vår enkle, fiktive aksjeindeks `StockIndex` har et navn (`String`), indeks (`double`) og en liste med `Stock`-objektene som er inkludert i indeksen. Indeksen beregnes ut i fra aksjeprisene den "observerer", og vil være lik summen av disse. Når en av aksjeprisene øker eller synker, må tilstanden til `StockIndex`-objektet holdes konsistent med denne utviklingen. Dette lar seg gjøre ved at `StockIndex` observerer én eller flere `Stock`-objekter. Klassen skal ha følgende metoder:
- `StockIndex(String name, Stock... stocks)` - konstruktør som tar inn ingen, én eller flere aksjer (`Stock`-objekter). `Stock`-parameteret defineres som et såkalt [varargs-parameter](https://www.ntnu.no/wiki/display/tdt4100/Varargs+-+variabelt+antall+argumenter). NB: `StockIndex`-objektet skal holdes oppdatert på aksjeprisene allerede fra det er instansiert. Dersom en indeks instansieres uten `Stock`-objekter, skal aksjeindeksen være `0`. Verken navnet eller noen av `Stock`-argumentene kan være `null`.
- `void addStock(Stock stock)` - metode for å legge til en aksjepris i indeksen. Argumentet kan ikke være `null`.
- `void removeStock(Stock stock)` - metode for å fjerne en aksjepris fra indeksen.
- `double getIndex()` - hentemetode for indeksen.
I tillegg må `StockIndex`-klassen selvsagt implementere `StockListener` og dermed også lyttermetoden `stockPriceChanged`, slik at indeksen kan holdes oppdatert.
Testkode for denne oppgaven finner du her: [oving6/stock/StockIndexTest.java](../../src/test/java/oving6/stock/StockIndexTest.java).
## Ekstraoppgaver
I en del sammenhenger vil du ikke være interessert i alle småendringer i en aksjepris, men interessert i endringer utenfor et visst område eller av en viss størrelse. Kanskje vil du kjøpe aksjer hvis det er billig nok, ønsker å selge dersom prisen blir høy nok eller ønsker å vite om større endringer som kan være signal om viktige prisendringer. Så for å unngå å sende ut mange uinteressante prisoppdateringer, er det aktuelt med to typer utvidelser av `Stock`-klassen. I begge tilfellene bruker men en egen `addStockListener`-metode for å registrere lytteren og hva slags endring man er interessert i. Implementér utvidelsen(e) i en subklasse som du kaller `SmartStock`. Merk at denne utvidelsen av `Stock` ikke er så relevant å bruke sammen med `StockIndex`, siden den da vil miste noen oppdateringer og dermed kunne risikere å være inkonsistent innimellom.
### Pris*intervall*
I denne utvidelsen skal du støtte lyttere som ønsker å få beskjed kun dersom `Stock`-objektets pris settes utenfor et gitt intervall. Følgende metode må implementeres:
- `void addStockListener(StockListener, double min, double max)` - metode som legger til lyttere med krav til prisintervall.
Lyttere som er registrert med denne metoden skal bare varsles dersom `Stock`-objektets pris endres til en verdi utenfor det angitte intervallet. Hint: Bruk en `Map<StockListener, Double[]>`-felt til å holde oversikt over intervallene, eventuelt flere lister eller andre datastrukturer.
### Pris*differanse*
I denne utvidelsen skal du støtte lyttere som ønsker å få beskjed kun når akkumulerte endringer av `Stock`-objektets pris er større enn en gitt differanse. Følgende metode må implementeres:
- `void addStockListener(StockListener, double difference)` - metode som legger til lyttere med krav til prisdifferanse.
Et viktig poeng med denne er varianter er hvilke tidligere verdien som skal gis til lyttermetoden `stockPriceChanged` sitt andre argument. Denne verdien skal være den forrige verdien som ble rapportert, som kan være en annen enn den forrige prisverdien. Anta f.eks. at en lytter registreres med `10` som prisdifferanse og at aksjeprisen starter som `110` og så endres til `118` og videre til `121`. Da skal lyttermetoden `stockPriceChanged` kalles med `110` som gammel verdi og `121` som ny verdi, fordi dette sett fra lytterens perspektiv er forrige verdi den fikk vite om. En annen lytter som var registrert med prisdifferansen `5`, ville fått beskjed allerede ved første endring og da med `110` som gammel verdi og `118` som ny, men den ville ikke få beskjed om endringen fra `118` til `121`, fordi differansen da er for liten. Dersom prisen endrer seg videre til `124`, vil lytteren få beskjed og da med `118` som gammel verdi.
Testkode for denne oppgaven finner du her: [oving6/stock/SmartStockTest.java](../../src/test/java/oving6/stock/SmartStockTest.java).
Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

@@ -0,0 +1,23 @@
@startuml highscore-list
skinparam dpi 400
class HighscoreList {
- int maxSize
- List<Integer> results
+ HighscoreList(int)
+ int size()
+ int getElement(int)
+ void addResult(int)
+ void addHighscoreListListener(HighscoreListListener)
+ void removeHighscoreListListener(HighscoreListListener)
}
interface HighscoreListListener {
void listChanged(HighscoreList, int)
}
HighscoreList -u-> "highscoreListListeners: *" HighscoreListListener
@enduml
Binary file not shown.

After

Width:  |  Height:  |  Size: 194 KiB

+31
View File
@@ -0,0 +1,31 @@
@startuml hl-program
skinparam dpi 400
class HighscoreList {
- int maxSize
- List<Integer> results
+ HighscoreList(int)
+ int size()
+ int getElement(int)
+ void addResult(int)
+ void addHighscoreListListener(HighscoreListListener)
+ void removeHighscoreListListener(HighscoreListListener)
}
interface HighscoreListListener {
void listChanged(HighscoreList, int)
}
class HighscoreListProgram {
- HighscoreList highscoreList
+ void init()
+ void run()
}
HighscoreList -u-> "highscoreListListeners: *" HighscoreListListener
HighscoreListProgram ..l|> HighscoreListListener : "\t\t"
@enduml
+41
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).
@@ -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).
+65
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).
+46
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.
+63
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).
+51
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

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

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

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

+14
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

+33
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

+44
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
+6
View File
@@ -11,6 +11,7 @@
<properties> <properties>
<maven.compiler.source>25</maven.compiler.source> <maven.compiler.source>25</maven.compiler.source>
<maven.compiler.target>25</maven.compiler.target> <maven.compiler.target>25</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties> </properties>
<dependencies> <dependencies>
@@ -24,6 +25,11 @@
<artifactId>junit-jupiter</artifactId> <artifactId>junit-jupiter</artifactId>
<version>6.0.1</version> <version>6.0.1</version>
</dependency> </dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>33.5.0-jre</version>
</dependency>
</dependencies> </dependencies>
<build> <build>
+1
View File
@@ -7,4 +7,5 @@ open module ovinger {
requires javafx.controls; requires javafx.controls;
requires javafx.fxml; requires javafx.fxml;
requires javafx.graphics; requires javafx.graphics;
requires com.google.common;
} }
+4
View File
@@ -0,0 +1,4 @@
package oving1;
public class Account {
}
+64
View File
@@ -0,0 +1,64 @@
package oving1;
public class LineEditor {
String text = "";
int insertionIndex = 0;
LineEditor() {
}
void left() {
insertionIndex -= 1;
}
void right() {
insertionIndex += 1;
}
void insertString(String s) {
String a = text.substring(0, insertionIndex - 1);
String b = text.substring(insertionIndex, text.length());
text = a + s + b;
}
void deleteLeft() {
if (insertionIndex < 1) {
return;
}
String a = text.substring(0, insertionIndex - 2);
String b = text.substring(insertionIndex, text.length());
text = a + b;
}
void deleteRight() {
if (insertionIndex >= text.length() - 1) {
return;
}
String a = text.substring(0, insertionIndex - 1);
String b = text.substring(insertionIndex + 1, text.length());
text = a + b;
}
String getText() {
return text;
}
void setText(String s) {
text = s;
}
int getInsertionIndex() {
return insertionIndex;
}
void setInsertionIndex(int i) {
insertionIndex = i;
}
@Override
public String toString() {
String a = text.substring(0, insertionIndex - 1);
String b = text.substring(insertionIndex, text.length());
return a + "|" + b;
}
}
+4
View File
@@ -0,0 +1,4 @@
package oving1;
public class Location {
}
+6
View File
@@ -0,0 +1,6 @@
// class Main {
// public static void main(String[] args) {
// System.out.println("Hello, World!");
// LineEditor line_editor = new LineEditor("test", 0);
// }
// }
+87
View File
@@ -0,0 +1,87 @@
package oving1;
public class Rectangle {
int x1;
int y1;
int x2;
int y2;
public Rectangle(int x1, int y1, int x2, int y2) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
}
int getMinX() {
return Math.min(x1, x2);
}
int getMinY() {
return Math.min(y1, y2);
}
int getMaxX() {
return Math.max(x1, x2);
}
int getMaxY() {
return Math.max(y1, y2);
}
int getWidth() {
return getMaxX() - getMinX();
}
int getHeight() {
return getMaxY() - getMinY();
}
boolean isEmpty() {
return getWidth() == 0 || getHeight() == 0;
}
boolean contains(int x, int y) {
return x >= getMinX() && x <= getMaxX() && y >= getMinY() && y <= getMaxY();
}
boolean contains(Rectangle rect) {
return contains(rect.x1, rect.y1) && contains(rect.x2, rect.y1) && contains(rect.x1, rect.y2)
&& contains(rect.x2, rect.y2);
}
boolean add(int x, int y) {
int px1 = x1;
int px2 = x2;
int py1 = y1;
int py2 = y2;
x1 = Math.min(x, x1);
x2 = Math.max(x, x2);
y1 = Math.min(y, y1);
y2 = Math.max(y, y2);
return px1 == x1 && px2 == x2 && py1 == y1 && py2 == y2;
}
boolean add(Rectangle rect) {
return add(rect.x1, rect.y1) || add(rect.x2, rect.y1) || add(rect.x1, rect.y2) || add(rect.x2, rect.y2);
}
Rectangle union(Rectangle rect) {
int px1 = x1;
int px2 = x2;
int py1 = y1;
int py2 = y2;
add(rect);
Rectangle r = new Rectangle(x1, y1, x2, y2);
x1 = px1;
x2 = px2;
y1 = py1;
y2 = py2;
return r;
}
@Override
public String toString() {
return "";
}
}
+63
View File
@@ -0,0 +1,63 @@
package oving1;
public class StopWatch {
boolean active = false;
int time = 0;
int ticks = 0;
int lapTime = 0;
int lap = 0;
int lastLapTime = -1;
public StopWatch() {
}
boolean isStarted() {
return active;
}
boolean isStopped() {
return !active;
}
int getTicks() {
return ticks;
}
int getTime() {
return time;
}
int getLapTime() {
return lapTime;
}
int getLastLapTime() {
return lastLapTime;
}
void tick(int ticks) {
this.ticks += ticks;
if (isStopped()) {
return;
}
}
void start() {
active = true;
}
void lap() {
lap += 1;
lastLapTime = lapTime;
lapTime = 0;
}
void stop() {
active = false;
}
@Override
public String toString() {
return "";
}
}
@@ -0,0 +1,4 @@
package oving1;
public class UpOrDownCounter {
}
+7
View File
@@ -0,0 +1,7 @@
package oving2;
class Main {
public static void main(String[] args) {
Person p = new Person();
}
}
+119
View File
@@ -0,0 +1,119 @@
package oving2;
import java.util.Date;
import com.google.common.net.InternetDomainName;
public class Person {
private String name;
private String email;
private Date birthday;
private char gender;
private String SSN;
public Person() {
}
public String getName() {
return name;
}
public String getEmail() {
return email;
}
public Date getBirthday() {
return birthday;
}
public char getGender() {
return gender;
}
public String getSSN() {
return SSN;
}
public void setName(String name) {
int i = name.indexOf(' ');
if (i == -1) {
throw new IllegalArgumentException("Did not find both prename and surname.");
}
String prename = name.substring(0, i);
String surname = name.substring(i + 1, name.length());
if (prename.contains(" ") || surname.contains(" ")) {
throw new IllegalArgumentException("There should only be one space which separates prename and surname.");
}
if (prename.length() < 2 || surname.length() < 2) {
throw new IllegalArgumentException("Prename or surname should not be less than 2 characters.");
}
if (!isAlphabetic(prename) || !isAlphabetic(surname)) {
throw new IllegalArgumentException("Prename and surname should only contain letters.");
}
this.name = name;
}
private boolean isAlphabetic(String s) {
char[] chars = name.toCharArray();
for (char c : chars) {
if (!Character.isLetter(c)) {
return false;
}
}
return true;
}
public void setEmail(String email) {
if (email != null) {
throw new IllegalArgumentException("Email is already set.");
}
if (name == null) {
throw new IllegalArgumentException("Name needs to be set before email.");
}
int i = email.indexOf('@');
if (i == -1) {
throw new IllegalArgumentException("Email should contain 1 @");
}
String[] names = name.split(" ");
String first = email.substring(0, i);
String second = email.substring(i + 1, email.length());
if (first.contains("@") || second.contains("@")) {
throw new IllegalArgumentException("Email should contain 1 @");
}
if (first != names[0] + "." + names[1]) {
throw new IllegalArgumentException("Email username should be on the form: \"prename.surname\".");
}
boolean isDomain = true;
try {
InternetDomainName d = InternetDomainName.from(second);
isDomain = d.hasPublicSuffix();
} catch (IllegalArgumentException e) {
isDomain = false;
}
if (!isDomain) {
throw new IllegalArgumentException("Email domain is not on the form: \"local.domain\"");
}
this.email = email;
}
void setBirthday(Date date) {
Date now = new Date();
if (now.before(date)) {
throw new IllegalArgumentException("Birthday cannot be set to a future date.");
}
birthday = date;
}
void setGender(char c) {
if (c != 'M' && c != 'F' && c != '\0') {
throw new IllegalArgumentException("Invalid gender value.");
}
gender = c;
}
void setSSN(String s) {
}
}
+65
View File
@@ -0,0 +1,65 @@
package oving2;
public class Vehicle {
private char vehicleType;
private char fuelType;
private String registrationNumber;
Vehicle(char vehicleType, char fuelType, String regstrationNumber) {
setVehicleType(vehicleType);
setFuelType(fuelType);
setRegistrationNumber(registrationNumber);
}
char getVehicleType() {
return vehicleType;
}
char getFuelType() {
return fuelType;
}
String getRegistrationNumber() {
return registrationNumber;
}
void setVehicleType(char vehicleType) {
if (vehicleType != 'M' && vehicleType != 'C') {
throw new IllegalArgumentException("Invalid vehicle type, found: \"" + vehicleType + "\"");
}
this.vehicleType = vehicleType;
}
void setFuelType(char fuelType) {
if (fuelType != 'H' && fuelType != 'E' && fuelType != 'D' && fuelType != 'G') {
throw new IllegalArgumentException("Invalid vehicle type, found: \"" + fuelType + "\"");
}
this.fuelType = vehicleType;
}
void setRegistrationNumber(String registrationNumber) {
String code = registrationNumber.substring(0, 2);
if (fuelType == 'E' && code != "EL" && code != "EK") {
throw new IllegalArgumentException(
"Electric vehicles should have registraition number starting with EL or EK, got: "
+ registrationNumber + "\"");
} else if (fuelType == 'H' && code != "HY") {
throw new IllegalArgumentException(
"Hydrogen vehicles should have registraition number starting with HY, got: " + registrationNumber
+ "\"");
} else if ((fuelType == 'D' || fuelType == 'G') && (code == "EL" || code == "EK" || code == "HY")) {
throw new IllegalArgumentException(
"Diesel or gas vehicles should have reigstartion number not starting with EL, EK, or HY.");
}
if (vehicleType == 'C' && registrationNumber.length() != 7) {
throw new IllegalArgumentException(
"Cars have a registrationNumber of length 7" + registrationNumber + "\"");
}
if (vehicleType == 'M' && registrationNumber.length() != 6) {
throw new IllegalArgumentException(
"Motorbikes have a registrationNumber of length 6" + registrationNumber + "\"");
}
this.registrationNumber = registrationNumber;
}
}
+12
View File
@@ -0,0 +1,12 @@
# Teori
- Hva er en synlighetsmodifikator?
En synlighetsmodifikator endrer på hvem som har tilgang på noe. Enten det er en attributt, metode, eller klasse.
- Hva er forskjellen på private og public og når brukes de?
Hvis man setter en attributt til private vil den kun være direkte tilgjengelig innad i den klassen den blir definert. Hvis den blir satt til public er ikke dette lenger tilfellet og man kan direkte modifisere og lese den utenfor klassen slik som når man har en instans av klassen.
# Oppgaver
## Person
### Del 2
- Navnet kan være delt i fornavn og fullt navn.
- Man kan enten gjøre det slik at man må gi inn begge deler samtidig, eller at man må gi inn det ene, og at man da finner ut av den andre basert på den ene. Man kan og ha at man angir det hver for seg uten at man antar at de er sammmenkoblet.
+63
View File
@@ -0,0 +1,63 @@
package oving3;
import java.util.Stack;
public class RPNCalc {
private Stack<Double> stack = new Stack<>();
RPNCalc() {
}
void push(double n) {
stack.push(n);
}
double pop() {
if (stack.isEmpty()) {
return Double.NaN;
}
return stack.pop();
}
double peek(int i) {
int id = stack.size() - i;
if (id < 0 || id >= stack.size()) {
return Double.NaN;
}
return stack.get(id);
}
int getSize() {
return stack.size();
}
void performOperation(char c) {
if (stack.size() <= 1) {
return;
}
double a = stack.pop();
double b = stack.pop();
switch (c) {
case '+':
stack.push(a + b);
break;
case '-':
stack.push(a - b);
break;
case '*':
stack.push(a * b);
break;
case '/':
stack.push(a / b);
break;
default:
throw new IllegalArgumentException("Invalid operation");
}
}
@Override
public String toString() {
return stack.toString();
}
}
+13 -8
View File
@@ -8,7 +8,8 @@ package oving3.card;
*/ */
public class Card { public class Card {
// TODO: Add fields here private char suit;
private int face;
/** /**
* The constructor of the {@code Card} class initializes the suit and face of * The constructor of the {@code Card} class initializes the suit and face of
@@ -26,23 +27,28 @@ public class Card {
* @see CardTest#testConstructor() * @see CardTest#testConstructor()
*/ */
public Card(char suit, int face) { public Card(char suit, int face) {
// TODO: Implement this constructor if (suit != 'S' && suit != 'H' && suit != 'D' && suit != 'C') {
throw new IllegalArgumentException("Illegal value for suit");
}
if (face < 1 || face > 13) {
throw new IllegalArgumentException("Illegal value for face");
}
this.suit = suit;
this.face = face;
} }
/** /**
* @return the suit of the card * @return the suit of the card
*/ */
public char getSuit() { public char getSuit() {
// TODO: Implement this method return suit;
return '\0';
} }
/** /**
* @return the face of the card * @return the face of the card
*/ */
public int getFace() { public int getFace() {
// TODO: Implement this method return face;
return 0;
} }
/** /**
@@ -54,8 +60,7 @@ public class Card {
*/ */
@Override @Override
public String toString() { public String toString() {
// TODO: Implement this method return suit + Integer.toString(face);
return null;
} }
public static void main(String[] args) { public static void main(String[] args) {
+43
View File
@@ -0,0 +1,43 @@
package oving3.card;
import java.util.ArrayList;
public class CardDeck {
private ArrayList<Card> deck = new ArrayList<>();
CardDeck(int n) {
if (n < 0 || n > 13) {
throw new IllegalArgumentException("value of n needs to be >= 1 and <= 13");
}
char[] suits = { 'S', 'H', 'D', 'C' };
for (char suit : suits) {
for (int face = 1; face <= n; face++) {
deck.add(new Card(suit, face));
}
}
}
int getCardCount() {
return deck.size();
}
Card getCard(int n) {
return new Card('S', 1);
}
void shufflePerfectly() {
int n = deck.size();
if (n < 2) {
return;
}
ArrayList<Card> shuffledDeck = new ArrayList<>();
for (int i = 0; i < n / 2; i++) {
shuffledDeck.add(deck.get(i));
shuffledDeck.add(deck.get(n / 2 + i));
}
if (n % 2 != 0) {
shuffledDeck.add(deck.get(n - 1));
}
deck = shuffledDeck;
}
}
@@ -42,7 +42,9 @@ public class CoffeeCup {
public void drinkCoffee(double volume) { public void drinkCoffee(double volume) {
if (!this.isValidVolume(volume) || !this.canDrink(volume)) { if (!this.isValidVolume(volume) || !this.canDrink(volume)) {
throw new IllegalArgumentException("You cannot drink that much coffee!"); throw new IllegalArgumentException(
"You cannot drink that much coffee! Trying to drink: " + volume + ", cup can hold: " + capacity
+ ", cup has: " + currentVolume);
} }
this.currentVolume -= volume; this.currentVolume -= volume;
@@ -21,10 +21,13 @@ public class CoffeeCupProgram {
this.cup.fillCoffee(32.5); this.cup.fillCoffee(32.5);
this.cup.drinkCoffee(Math.ceil(this.r.nextDouble() * 38.9)); this.cup.drinkCoffee(Math.ceil(this.r.nextDouble() * 38.9));
this.cup.drinkCoffee(Math.ceil(this.r.nextDouble() * 42)); this.cup.drinkCoffee(Math.ceil(this.r.nextDouble() * 42));
this.cup.increaseCupSize(17); this.cup.increaseCupSize(17 + 300);
this.cup.fillCoffee(35);
this.cup.drinkCoffee(40); this.cup.drinkCoffee(40);
this.cup.fillCoffee(42 + 20.5);
this.cup.drinkCoffee(Math.ceil(this.r.nextDouble() * 42)); this.cup.drinkCoffee(Math.ceil(this.r.nextDouble() * 42));
this.cup.drinkCoffee(Math.floor(this.r.nextDouble() * 20.5)); this.cup.drinkCoffee(Math.floor(this.r.nextDouble() * 20.5));
this.cup.increaseCupSize(170);
this.cup.fillCoffee(32.5); this.cup.fillCoffee(32.5);
this.cup.drinkCoffee(Math.ceil(this.r.nextDouble() * 38.9)); this.cup.drinkCoffee(Math.ceil(this.r.nextDouble() * 38.9));
this.cup.drinkCoffee(Math.ceil(this.r.nextDouble() * 42)); this.cup.drinkCoffee(Math.ceil(this.r.nextDouble() * 42));
@@ -34,6 +37,8 @@ public class CoffeeCupProgram {
private void part2() { private void part2() {
this.cup = new CoffeeCup(40.0, 20.5); this.cup = new CoffeeCup(40.0, 20.5);
this.r = new Random(987_654_321L); this.r = new Random(987_654_321L);
this.cup.increaseCupSize(10000);
this.cup.fillCoffee(9000);
this.cup.drinkCoffee(Math.floor(this.r.nextDouble() * 20.5)); this.cup.drinkCoffee(Math.floor(this.r.nextDouble() * 20.5));
this.cup.fillCoffee(Math.floor(this.r.nextDouble() * 30)); this.cup.fillCoffee(Math.floor(this.r.nextDouble() * 30));
this.cup.drinkCoffee(Math.ceil(this.r.nextDouble() * 38.9)); this.cup.drinkCoffee(Math.ceil(this.r.nextDouble() * 38.9));
+20
View File
@@ -0,0 +1,20 @@
# Teori
## RPN
### Del 2
Hvilken type unntak vil det være naturlig å bruke?
IllegalStateException
Hvilke fordeler og ulemper ser du for dette alternativet?
Resistans til at programmet krasjer, men problemet er at alternativet er dårlig data.
Hva vil tilsvarende verdi for manglende operand for *-operasjonen (multiplikasjon) være? Hva med for / (divisjon)?
1 og 1. Det vil føre til ingen endring.
Hvordan kan du endre (evt. har du endret) grensesnittet for stack-operasjonene for å gjøre implementasjonen av disse enklere?
Ta inn en default.
Også her er et alternativ å utløse unntak. Hva tror du om det?
IllegalStateException kan brukes, men det er avhengig om man hvordan man ønsker at grensesnittet skal fungere, og hvor strengt det skal være. Det viktigste er at man er konsekvent.
+89
View File
@@ -0,0 +1,89 @@
package oving4;
import java.util.ArrayList;
public class Person {
private String name;
private char gender;
private Person mother;
private Person father;
private ArrayList<Person> children;
Person() {
}
String getName() {
return name;
}
char getGender() {
return gender;
}
Person getMother() {
return mother;
}
Person getFather() {
return father;
}
int getChildCount() {
return children.size();
}
Person getChild(int n) {
if (n < 0 || n >= children.size()) {
throw new IllegalArgumentException("invalid index");
}
return children.get(n);
}
void addChild(Person p) {
if (isMale()) {
p.father = this;
} else {
p.mother = this;
}
children.add(p);
}
void removeChild(Person p) {
for (int i = 0; i < children.size(); i++) {
if (p.name == children.get(i).name) {
children.remove(i);
return;
}
}
if (p.isMale() && this.father == p) {
this.father = null;
return;
}
if (p.isFemale() && this.mother == p) {
this.mother = null;
return;
}
}
void setMother(Person p) {
mother = p;
}
void setFather(Person p) {
father = p;
}
boolean isMale() {
if (gender == 'M') {
return true;
}
if (gender == 'F') {
return false;
}
throw new IllegalArgumentException("Illegal gender");
}
boolean isFemale() {
return !isMale();
}
}
View File
@@ -0,0 +1,73 @@
package oving4.testing;
public class CoffeeCup {
private double capacity;
private double currentVolume;
public CoffeeCup() {
this.capacity = 0.0;
this.currentVolume = 0.0;
}
public CoffeeCup(double capacity, double currentVolume) {
if (!CoffeeCup.isValidCapacity(capacity)) {
throw new IllegalArgumentException("Illegal capacity given.");
}
this.capacity = capacity;
if (!this.isValidVolume(currentVolume)) {
throw new IllegalArgumentException("Illegal volume given.");
}
this.currentVolume = currentVolume;
}
public double getCapacity() {
return this.capacity;
}
public double getCurrentVolume() {
return this.currentVolume;
}
private static boolean isValidCapacity(double capacity) {
return capacity >= 0.0;
}
public void increaseCupSize(double biggerCapacity) {
if (CoffeeCup.isValidCapacity(biggerCapacity)) {
this.capacity += biggerCapacity;
}
}
private boolean isValidVolume(double volume) {
return volume <= this.capacity && volume >= 0.0;
}
private boolean canDrink(double volume) {
return volume <= this.currentVolume;
}
public void drinkCoffee(double volume) {
if (!this.isValidVolume(volume) || !this.canDrink(volume)) {
throw new IllegalArgumentException("You cannot drink that much coffee!");
}
this.currentVolume -= volume;
}
public void fillCoffee(double volume) {
if (!this.isValidVolume(this.currentVolume + volume)) {
throw new IllegalArgumentException(
"You just poured coffee all over the table. Good job.");
}
this.currentVolume += volume;
}
@Override
public String toString() {
return String.format("[CoffeeCup capacity=%.2f, currentVolume=%.2f]", this.capacity,
this.currentVolume);
}
}
@@ -0,0 +1,66 @@
package oving5;
import java.util.Iterator;
import java.util.function.BinaryOperator;
public class BinaryComputingIterator {
private Iterator<Double> iterator1;
private Iterator<Double> iterator2;
private Double default1;
private Double default2;
private BinaryOperator<Double> operator;
BinaryComputingIterator(Iterator<Double> iterator1, Iterator<Double> iterator2, BinaryOperator<Double> operator) {
if (iterator1 == null || iterator2 == null || operator == null) {
throw new IllegalArgumentException("argument cannot be null");
}
this.iterator1 = iterator1;
this.iterator2 = iterator2;
this.operator = operator;
}
BinaryComputingIterator(Iterator<Double> iterator1, Iterator<Double> iterator2, Double default1, Double default2,
BinaryOperator<Double> operator) {
if (iterator1 == null || iterator2 == null || operator == null) {
throw new IllegalArgumentException("argument cannot be null");
}
this.iterator1 = iterator1;
this.iterator2 = iterator2;
this.operator = operator;
this.default1 = default1;
this.default2 = default2;
}
boolean hasNext() {
if (!iterator1.hasNext() && !iterator2.hasNext()) {
return false;
}
if ((iterator1.hasNext() || default1 != null) && (iterator2.hasNext() || default2 != null)) {
return true;
}
return false;
}
Double next() {
if (hasNext()) {
Double a = 0.0;
if (iterator1.hasNext()) {
a = iterator1.next();
} else {
a = default1;
}
Double b = 0.0;
if (iterator2.hasNext()) {
b = iterator2.next();
} else {
b = default2;
}
return operator.apply(a, b);
}
throw new IllegalArgumentException("empty things");
}
}
+65
View File
@@ -0,0 +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() {
}
boolean addOperator(char op, BinaryOperator<Double> f) {
if (ops.containsKey(op) || f == null) {
return false;
}
ops.put(op, f);
return true;
}
void push(double n) {
stack.push(n);
}
double pop() {
if (stack.isEmpty()) {
return Double.NaN;
}
return stack.pop();
}
double peek(int i) {
int id = stack.size() - i;
if (id < 0 || id >= stack.size()) {
return Double.NaN;
}
return stack.get(id);
}
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
+10
View File
@@ -0,0 +1,10 @@
package oving5.card;
public class Card {
Card(char suit, int card) {
}
char getSuit() {
return 'A';
}
}
@@ -0,0 +1,46 @@
package oving5.debugging;
import java.util.Iterator;
import java.util.NoSuchElementException;
public class StringMergingIterator implements Iterator<String> {
private final Iterator<String> first;
private final Iterator<String> second;
private boolean turnSwitch = true;
public StringMergingIterator(Iterator<String> first, Iterator<String> second) {
this.first = first;
this.second = second;
}
@Override
public boolean hasNext() {
return this.first.hasNext() || this.second.hasNext();
}
@Override
public String next() {
if (!this.hasNext()) {
throw new NoSuchElementException();
}
String result = null;
if (this.turnSwitch && this.first.hasNext()) {
result = this.first.next();
} else if (!this.turnSwitch && this.second.hasNext()) {
result = this.second.next();
} else {
if (this.first.hasNext()) {
result = this.first.next();
} else if (this.second.hasNext()) {
result = this.second.next();
} else {
throw new NoSuchElementException();
}
}
return result;
}
}
@@ -0,0 +1,40 @@
package oving5.debugging;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class StringMergingIteratorProgram {
public static void main(String[] args) throws Exception {
Iterator<String> one = List.of("a", "b").iterator();
Iterator<String> two = List.of("c", "d", "e").iterator();
StringMergingIterator mergeIterator = new StringMergingIterator(one, two);
List<String> values = new ArrayList<>();
while (mergeIterator.hasNext()) {
values.add(mergeIterator.next());
}
List<String> expectedOutput = List.of("a", "c", "b", "d", "e");
if (values.size() != expectedOutput.size()) {
throw new Exception(
"The merged output did not contain the expected number of values. Try using "
+ "the VS Code debugger to see the difference between the lists");
}
for (int i = 0; i < expectedOutput.size(); i++) {
if (!values.get(i).equals(expectedOutput.get(i))) {
throw new Exception(
"The iterator did not correctly merge the output. Try using the VS Code "
+ "debugger to see the difference between the lists");
}
}
System.out.println(
"Success! StringMergingIterator correctly merged the output of the two lists");
}
}
View File
+10
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);
}

Some files were not shown because too many files have changed in this diff Show More