Skip to content

Commit

Permalink
Merge pull request #3 from tdt4100/add-oving2
Browse files Browse the repository at this point in the history
Add oving 2
  • Loading branch information
andreaoo authored Jan 19, 2026
2 parents 7154e48 + c7c4780 commit 0586d24
Show file tree
Hide file tree
Showing 18 changed files with 1,456 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ Denne mappen inneholder øvingstekster for TDT4100 - Objektorientert programmeri
| -------------------------------------------- | ----------------------------------------- |
| [Øving 0](./oppgavetekster/oving0/README.md) | Introduksjon og oppsett av Java |
| [Øving 1](./oppgavetekster/oving1/README.md) | Java-syntaks og objektorientert tankegang |
| [Øving 2](./oppgavetekster/oving2/README.md) | Innkapsling og validering |
38 changes: 38 additions & 0 deletions oppgavetekster/oving2/Account.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Innkapsling - Account-oppgave

Oppgaven er en innkapslet og litt utvidet variant av [Account-oppgaven](../oving1/Account.md) under temaet [Tilstand og oppførsel](https://www.ntnu.no/wiki/pages/viewpage.action?pageId=65937373), og stiller blant annet større krav til validering.

Dersom du ikke har gjort [Account-oppgaven](../oving1/Account.md), bør du gjøre den først. Løsningsforslag til denne oppgaven kommer til å bli tilgjengelig [her](https://git.ntnu.no/tdt4100/tdt4100-lf-25/blob/main/src/main/java/oving1/Account.java) etter siste demonstrasjonsfrist for øving 1.

Et `Account`-objekt inneholder data om beløpet som står på kontoen og rentefoten (prosentpoeng).

Begge verdiene skal oppgis og settes når objektet opprettes og ingen av verdiene kan være negative.

`Account`-klassen har metoder for å sette inn og ta ut beløp, og legge til påløpte renter, i tillegg til en konstruktør for å initialisere en ny konto. Alle disse skal utløse unntak av typen `IllegalArgumentException`, dersom et argument ikke tilfredstiller kravene som angis.

- `Account(double, double)` - konstruktøren skal ta inn startbeløpet og rentefoten (prosentpoeng). Ingen av disse kan være negative.
- `double getBalance()` - returnerer beløpet som står på kontoen.
- `double getInterestRate()` - returnerer renten på kontoen.
- `void setInterestRate(double)` - denne metoden tar inn en ikke-negativ verdi og setter renten til denne verdien.
- `void deposit(double)` - denne metoden tar inn et ikke-negativt beløp og øker konto-beløpet tilsvarende.
- `void withdraw(double)` - denne metoden tar inn et ikke-negativt beløp og minsker konto-beløpet tilsvarende. Dersom det nye konto-beløpet er negativt, så skal tilstanden ikke endre, og det skal utløses et unntak av typen `IllegalArgumentException`.
- `void addInterest()` - beregner renta og legger det til konto-beløpet.

## Leseliste

- [Gyldig tilstand](https://www.ntnu.no/wiki/display/tdt4100/Gyldig+tilstand) - Tilstanden til et objekt er verdien av alle attributtene. En viktig del av [oppførselen til et objekt](https://www.ntnu.no/wiki/pages/viewpage.action?pageId=65937373) er å sikre at tilstanden til objektet alltid er _gyldig_, dvs. at alle attributtene har gyldige/konsistente verdier.
- [Innkapsling](https://www.ntnu.no/wiki/display/tdt4100/Innkapsling) - Innkapsling er en programmeringsteknikk som har som formål å hindre direkte tilgang til tilstanden til et objekt fra objekter av andre klasser.
- [Koding av valideringsmetoder](https://www.ntnu.no/wiki/display/tdt4100/Koding+av+valideringsmetoder) - En valideringsmetode har som formål å sjekke om en eller flere verdier er gyldige, slik at dette kan sjekkes av f.eks. setter-metoder før tilsvarende attributter evt. settes.

## Del 1: Innkapsling og validering - Teori

Ta utgangspunkt i koden fra [Account](../oving1/Account.md)-klassen og besvar følgende spørsmål:

- Forklar hvorfor metodene over kan sies å være en komplett innkapsling av tilstanden?
- Er denne klassen data-orientert eller tjeneste-orientert? Begrunn svaret!

## Del 2 - Java-kode

Implementer endringene fra [Account](../oving1/Account.md)-klassen i den nye `Account`-klassen med oppførsel som beskrevet over.

Testkode for denne oppgaven finner du i [oving2/AccountTest.java](../../src/test/java/oving2/AccountTest.java).
38 changes: 38 additions & 0 deletions oppgavetekster/oving2/Encapsulation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Innkapsling og gyldig tilstand - Oppgave om innkapsling og validering av klasser

I denne oppgaven skal du velge tre oppgaver som du har gjort i øving 1 fra listen nedenfor, og innkapsle og validere disse klassene. Dersom du ikke har gjort tre oppgaver, bør du gjøre dette først. Løsningsforslag til øving 1 kommer til å befinne seg [her](https://git.ntnu.no/tdt4100/tdt4100-lf-25/blob/main/src/main/java/oving1) etter siste demonstrasjonsfrist for øving 1.

Skriv svar (stikkord/få, korte setninger) på spørsmål 1-4 (fra del 1 nedenfor) som kommentarer i koden din.

__Oppgaver__:

- [Digit-oppgave](../oving1/Digit.md) (Lett)
- [UpOrDownCounter-oppgave](../oving1/UpOrDownCounter.md) (Lett)
- [Location-oppgave](../oving1/Location.md) (Lett)
- [StopWatch-oppgave](../oving1/StopWatch.md) (Medium)
- [LineEditor-oppgave med fri peker](./LineEditor.md) (Vanskelig)

Merk at spesifikasjonen for [LineEditor](../oving1/LineEditor.md) er litt utvidet for denne oppgaven. Se [LineEditor-oppgave med fri peker](./LineEditor.md).

## Del 1: Innkapsling og validering - Teori

Ta utgangspunkt i (koden for) den originale klassen og besvar følgende spørsmål:

- Hvordan skal private og public brukes for at denne klassen skal være korrekt innkapslet?
- Hva slags validering bør legges til for å sikre gyldig tilstand?
- Hvilke metoder må evt. legges til?
- Vil du karakterisere denne klassen som data-orientert eller tjeneste-orientert. Begrunn svaret!

## Del 2: Java-kode

Implementer endringene foreslått i punktene 1-3 og prøv ut klassene. Husk å kopiere koden din fra mappen i øving 1 til [`src/main/java/oving2`](../../src/main/java/oving2)!

Testkoder for denne oppgaven finner du her:

- [oving2/DigitTest.java](../../src/test/java/oving2/DigitTest.java).
- [oving2/UpOrDownCounterTest.java](../../src/test/java/oving2/UpOrDownCounterTest.java).
- [oving2/LocationTest.java](../../src/test/java/oving2/LocationTest.java).
- [oving2/StopWatchTest.java](../../src/test/java/oving2/StopWatchTest.java).
- [oving2/LineEditorTest.java](../../src/test/java/oving2/LineEditorTest.java).

Testkodene viser om du har innkapslet på samme måte som fagstaben har gjort. Din kode kan fungere selv om testene feiler, dersom du har valgt en løsere/strammere innkapsling iht. argumentasjonen i 1-3. Er du enig med hvordan fagstaben har gjort det?
16 changes: 16 additions & 0 deletions oppgavetekster/oving2/LineEditor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Gyldig tilstand - LineEditor-oppgave med fri peker

Oppgaven utvider [Tilstand og oppførsel - LineEditor-oppgave](../oving1/LineEditor.md) med validering.

Denne oppgaven tar utgangspunkt i [Tilstand og oppførsel - LineEditor-oppgave](../oving1/LineEditor.md) og utvider `LineEditor`-klassen med metoder for å endre teksten og tekstinnsettingsposisjonen direkte, så det blir enklere å gjøre om tilstanden til objektet.

Dersom du ikke har gjort [LineEditor-oppgaven](../oving1/LineEditor.md), bør du gjøre den først. Løsningsforslag til denne oppgaven kommer til å bli tilgjengelig [her](https://git.ntnu.no/tdt4100/tdt4100-lf-25/blob/main/src/main/java/oving1/LineEditor.java) etter siste demonstrasjonsfrist for øving 1.

Endringer:

- Når teksten endres skal tekstinnsettingsposisjonen settes til å være bak teksten.
- Det skal ikke være mulig å passere `null` som tekst.

Hvordan vil du implementere dette med én eller flere metoder, inkludert valideringsmetode(r), slik at en er sikret at `LineEditor`-objekter aldri blir ugyldige?

Testkode for denne oppgaven finner du her: [oving2/LineEditorTest.java](../../src/test/java/oving2/LineEditorTest.java).
59 changes: 59 additions & 0 deletions oppgavetekster/oving2/Person.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Innkapsling - Person-oppgave

Oppgaven handler om en `Person`-klasse, som håndterer informasjon om en person (navn, e-post, fødselsdato og kjønn) og implementerer innkapslingsmetoder med validering.

Et `Person`-objekt inneholder _navn_ (både fornavn og etternavn), _e-post_, _fødselsdag_ og _kjønn_:

- Navnet inneholder både fornavn og etternavn (og ingen mellomnavn), som begge må være på minst to bokstaver langt, navnene må være skilt med ett mellomrom og kun inneholde bokstaver.
- E-post-adressen (hvis den ikke er `null`) må være på formen `fornavn.etternavn@domene.landskode`, f.eks. `hallvard.trætteberg@ntnu.no` (en liste over landskoder finner du [her](http://pastebin.com/chG6WLWF)).
- Fødselsdagen skal være et dato-objekt (java.util.Date) og kan ikke være frem i tid.
- En persons kjønn skal kunne returneres som `'M'`, `'F'` eller `'\0'` (null-tegnet).

`Person`-klassen har tilgangsmetoder for å hente og sette tilstandene. Dersom et argument er ugyldig i seg selv, så skal unntaket `IllegalArgumentException` utløses.

- `void setName(String)` - oppdaterer navnet (fornavn og etternavn med mellomrom mellom), dersom det er gyldig i henhold til kravene over. Det er greit om navnet som settes, ikke stemmer med e-post-adressen. Kast et passende unntak dersom navnet er `null` eller dersom e-post-adressen allerede har blitt satt.
- `void setEmail(String)` - oppdaterer e-post-adressen, etter å ha sjekket at den stemmer med navnet. Kast et passende unntak dersom e-post-adressen er `null` eller dersom navnet enda ikke har blitt satt.
- `void setBirthday(Date)` - oppdaterer fødselsdatoen. Kast et passende unntak dersom fødselsdatoen er `null` eller ligger i fremtiden.
- `void setGender(char)` - oppdaterer kjønnet. Kast et passende unntak dersom kjønnet er ugyldig.

I tillegg til disse såkalte _setter_-metodene, så må `Person`-klassen ha tilsvarende _getter_-metoder.

## Leseliste

- [Gyldig tilstand](https://www.ntnu.no/wiki/display/tdt4100/Gyldig+tilstand) - Tilstanden til et objekt er verdien av alle attributtene. En viktig del av [oppførselen til et objekt](https://www.ntnu.no/wiki/pages/viewpage.action?pageId=65937373) er å sikre at tilstanden til objektet alltid er _gyldig_, dvs. at alle attributtene har gyldige/konsistente verdier.
- [Innkapsling](https://www.ntnu.no/wiki/display/tdt4100/Innkapsling) - Innkapsling er en programmeringsteknikk som har som formål å hindre direkte tilgang til tilstanden til et objekt fra objekter av andre klasser.
- [Koding av valideringsmetoder](https://www.ntnu.no/wiki/display/tdt4100/Koding+av+valideringsmetoder) - En valideringsmetode har som formål å sjekke om en eller flere verdier er gyldige, slik at dette kan sjekkes av f.eks. setter-metoder før tilsvarende attributter evt. settes.
- [String-klassen](https://www.ntnu.no/wiki/display/tdt4100/java.lang.String) - Siden gir en innføring i String-klassen og en oversikt over nyttig String-metoder.

## Del 1 – Java-kode

Implementer `Person`-klassen med stram innkapsling. Eventuelle hjelpemetoder for validering bør også ha stram innkapsling. Det kan være lurt å lese om [String-klassen](https://www.ntnu.no/wiki/display/tdt4100/java.lang.String) og dens metoder før du setter i gang.

Testkode for denne oppgaven finner du i [oving2/PersonTest.java](../../src/test/java/oving2/PersonTest.java).

Merk at din implementasjon må ligge i en pakke med samme navn som testkodens pakke. Pass derfor på at Person-klassen ligger i pakken `oving2`.

## Del 2 - Spørsmål om innkapsling

- Foreslå en alternativ innkapsling av navnet. Hint: del opp.
- Foreslå _to_ alternative strategier for å kapsle inn tilstand som er koblet slik navn og e-post er. Hint: 1) samtidig og 2) dekoble.

## Ekstraoppgave - Personnummer

Utvid klassen med en persons personnummer. Personnummeret kan ikke settes før kjønn og fødselsdag er satt.

Et personnummer består grovt sett av fødselsdatoen, et (vilkårlig) løpenummer og to kontrollsifre. Kontrollsifrene gjør det enklere å sjekke om et personnummer er ekte. Mer spesifikt er reglene for personnummer som følger:

- Et personnummer består av 11 siffer, med følgende struktur: **D1D2**M1M2**Y1Y2**N1N2N3**K1K2** (fet skrift for lesbarhet).
- De seks første sifrene, **D1D2**M1M2**Y1Y2**, tilsvarer fødselsdatoens dag (1-31), måned (1-12) og år (0-99).
- De tre neste sifrene, N1N2N3, kan antas å være vilkårlige, men N3 må være partall for kvinner og oddetall for menn.
- De to siste sifrene, K1K2, er kontrollsifre, som hver for seg beregnes ut fra de foregående sifrene. Formelen for dem begge er `11 – (VS % 11)`, hvor VS (veid sum) for `K1` er `D1*F1 + D2*F2 + … + N2*F8 + N3*F9` og `VS` for `K2` er `D1*G1 + D2*G2 + … + N3*G9 + K1*G10`. F’ene og G’ene er oppgitt i tabellen under. Dersom formelen gir tallet 11 så skal verdien 0 brukes isteden. Om både K1 og K2 stemmer med kontrollsifferne generert basert på formlene over, så er kontrollsifferne i personnummeret gyldig.

| | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| F | 3 | 7 | 6 | 1 | 8 | 9 | 4 | 5 | 2 | |
| G | 5 | 4 | 3 | 2 | 7 | 6 | 5 | 4 | 3 | 2 |

Implementer kode for å sette (med metoden `setSSN(String)`) og validere et gyldig personnummer. Du må også kaste passende unntak dersom brukeren prøver å endre tilstanden slik at personnummeret ville blitt ugyldig.

Testkode for denne oppgaven finner du i [oving2/PersonTest2.java](../../src/test/java/oving2/PersonTest2.java).
46 changes: 46 additions & 0 deletions oppgavetekster/oving2/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Øving 2: Innkapsling og vali⁣dering

**Øvingsmål**:

- Lære å innkapsle klasser og metoder etter god programmeringsskikk
- Lære å validere argumenter for å sikre gyldig tilstand

**Øvingskrav**:

- Kunne forstå og implementere hvordan en klasse best bør innkapsles
- Kunne skrive kode for å validere argumenter for å sikre gyldig tilstand
- Kunne utløse exceptions ved ugyldige argumenter i en metode

## Dette må du gjøre

### Del 1: Teori

Les [wikisiden om innkapsling](https://www.ntnu.no/wiki/display/tdt4100/Innkapsling) og svar på følgende:

- Hva er en **synlighetsmodifikator**?
- Hva er forskjellen på **private** og **public** og når brukes de?

Teori-oppgavene besvares i en tekstfil eller på papir, og gjennomgås med studass ved godkjenning.

### Del 2: Programmering

Velg minst 2 av følgende oppgaver:

- [Innkapsling og validering av 3 eksisterende klasser](./Encapsulation.md) (Varierende)
- [Account](./Account.md) (Lett)
- [Person](./Person.md) (Medium)
- [Vehicle](./Vehicle.md) (Medium)

Oppgavene for denne øvingen skal lagres i [`src/main/java/oving2`](../../src/main/java/oving2). Test-filene som kjøres for å teste koden ligger i [`src/test/java/oving2`](../../src/test/java/oving2).

Oppgavene er markert med en vanskeliggrad relativt til hverandre. Det er en god idé å begynne med de lettere oppgavene dersom du ikke er komfortabel med pensum så langt, men det er anbefalt å prøve seg på de vanskeligere oppgavene om du synes de første oppgavene er uproblematiske. Dersom du allerede føler deg trygg på punktene i øvingskravene kan du forsøke å gå rett på de vanskeligere oppgavene. Du er selvfølgelig velkommen til å løse flere oppgaver enn minstekravet, hvilket lurt gjøres med tanke på eksamen og et langt liv som programmerende.

Før du setter i gang kan det vært lurt å lese nevnte [wikiside om innkapsling](https://www.ntnu.no/wiki/display/tdt4100/Innkapsling) nøye. Forelesningene og tilhørende øvingsforelesning er selvsagt også lure å få med seg.

### 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 stud.ass innen én uke etter innleveringsfrist. Se for øvrig Blackboard-sidene for informasjon rundt organisering av øvingsopplegget og det tilhørende øvingsreglementet.
30 changes: 30 additions & 0 deletions oppgavetekster/oving2/Vehicle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Innkapsling - Vehicle-oppgave

Oppgaven handler om en `Vehicle`-klasse, som håndterer informasjon om et kjøretøy og implementerer innkapslingsmetoder med validering.

Et `Vehicle`-objekt inneholder type _kjøretøy_, _drivstoffet_ det bruker og _registreringsnummer_:

- Typen kan være enten motorsykkel eller bil.
- Drivstoffet kan være enten hydrogen, elektrisitet, diesel eller bensin. Kun biler kan gå på hydrogen.
- Gyldige registreringsnummeret avhenger av typen kjøretøy og drivstoff etter følgende regler:
- Kjøretøy som går på elektrisitet skal ha registreringsnummer som starter med bokstavene `"EL"` eller `"EK"`.
- Hydrogenbilene har registreringsnummer som begynner med bokstavene `"HY"`.
- Dieselkjøretøy og bensinkjøretøy har registreringsnummer som begynner på to bokstaver. De kan ikke være `"EK"`, `"EL"` eller `"HY"`. Bokstavene Æ, Ø og Å skal ikke brukes.
- For alle drivstoff gjelder det at det skal være brukt store bokstaver.
- Ellers så gjelder det at motorsykler har 4 sifre etter bokstavene, mens biler har 5.

Følgende metoder må implementeres:

- `Vehicle(char, char, String)` - Konstruktør der argument-rekkefølgen må være kjøretøystype, drivstofftype og registreringsnummer. Ved ugyldige argumenter utløses unntak av typen `IllegalArgumentException`.
- `char getFuelType()` - returnerer type drivstoff som følgende: `'H'` for hydrogen, `'E'` for elektrisitet, `'D'` for diesel eller `'G'` for bensin.
- `String getRegistrationNumber()` - returnerer registreringsnummeret.
- `void setRegistrationNumber(String)` - endrer registreringsnummeret dersom det er gyldig i henhold til kravene over, og utløser unntak av typen `IllegalArgumentException` dersom det ikke er gyldig.
- `char getVehicleType()` - returnerer kjøretøystype: `'M'` for motorsykkel, `'C'` for bil.

## Java-kode

Implementer `Vehicle`-klassen som beskrevet over med stram innkapsling. Eventuelle hjelpemetoder for validering bør også ha stram innkapsling. Det kan være lurt å lese om [String-klassen](https://www.ntnu.no/wiki/display/tdt4100/java.lang.String) og dens metoder før du setter i gang.

Testkode for denne oppgaven finner du i [oving2/VehicleTest.java](../../src/test/java/oving2/VehicleTest.java).

Merk at din implementasjon må ligge i en pakke med samme navn som testkodens pakke. Pass derfor på at Vehicle-klassen ligger i pakken `oving2`.
Empty file added src/main/java/oving2/.gitkeep
Empty file.
68 changes: 68 additions & 0 deletions src/test/java/oving2/AccountTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package oving2;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

public class AccountTest {

private static final double epsilon = 0.000001;

private Account account;

@BeforeEach
public void setUp() {
account = new Account(100, 5);
}

@Test
@DisplayName("Private fields")
public void testPrivateFields() {
TestHelper.checkIfFieldsPrivate(Account.class);
}

@Test
public void testConstructor() {
assertEquals(100.0d, account.getBalance(), epsilon);
assertEquals(5.0d, account.getInterestRate(), epsilon);

assertThrows(IllegalArgumentException.class, () -> {
new Account(-1, 5);
});
assertThrows(IllegalArgumentException.class, () -> {
new Account(100, -1);
});
}

@Test
public void testSetInterestRate() {
account.setInterestRate(7);
assertEquals(7, account.getInterestRate(), epsilon);

assertThrows(IllegalArgumentException.class, () -> {
account.setInterestRate(-2);
});
}

@Test
public void testDeposit() {
account.deposit(100);
assertEquals(200.0d, account.getBalance(), epsilon);

assertThrows(IllegalArgumentException.class, () -> {
account.deposit(-50);
});
}

@Test
public void testWithdraw() {
account.withdraw(50);
assertEquals(50.0d, account.getBalance(), epsilon);

assertThrows(IllegalArgumentException.class, () -> {
account.withdraw(150);
});
}
}
Loading

0 comments on commit 0586d24

Please sign in to comment.