Skip to content

Commit

Permalink
Merge pull request #5 from tdt4100/add-oving4
Browse files Browse the repository at this point in the history
Add oving 4
  • Loading branch information
andreaoo authored Feb 2, 2026
2 parents 9f58e12 + 7dd68c1 commit 6193e26
Show file tree
Hide file tree
Showing 41 changed files with 1,702 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ Denne mappen inneholder øvingstekster for TDT4100 - Objektorientert programmeri
| [Øving 1](./oppgavetekster/oving1/README.md) | Java-syntaks og objektorientert tankegang |
| [Øving 2](./oppgavetekster/oving2/README.md) | Innkapsling og validering |
| [Øving 3](./oppgavetekster/oving3/README.md) | Klasser og testing |
| [Øving 4](./oppgavetekster/oving4/README.md) | Objektstrukturer |
26 changes: 26 additions & 0 deletions oppgavetekster/oving4/Card.md
Original file line number Diff line number Diff line change
@@ -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 changes: 62 additions & 0 deletions oppgavetekster/oving4/Partner.md
Original file line number Diff line number Diff line change
@@ -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 changes: 134 additions & 0 deletions oppgavetekster/oving4/Person.md
Original file line number Diff line number Diff line change
@@ -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 changes: 61 additions & 0 deletions oppgavetekster/oving4/README.md
Original file line number Diff line number Diff line change
@@ -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.
Loading

0 comments on commit 6193e26

Please sign in to comment.