Skip to content

Add oving 6 #7

Merged
merged 1 commit into from
Feb 23, 2026
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions oppgavetekster/oving6/HighscoreList.md
Original file line number Diff line number Diff line change
@@ -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 changes: 136 additions & 0 deletions oppgavetekster/oving6/Logger.md
Original file line number Diff line number Diff line change
@@ -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 changes: 54 additions & 0 deletions oppgavetekster/oving6/Office.md
Original file line number Diff line number Diff line change
@@ -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 changes: 44 additions & 0 deletions oppgavetekster/oving6/README.md
Original file line number Diff line number Diff line change
@@ -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.
Loading