You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 10 Next »

Dersom du mener at opplysninger mangler i en oppgaveformulering, gjør kort rede for de antagelser og forutsetninger som du finner nødvendig. Hvis du i en del er bedt om å implementere klasser og metoder og du ikke klarer det (helt eller delvis), så kan du likevel bruke dem i senere deler.

En oversikt over klasser og metoder for alle oppgavene er gitt i vedlegg 1. Kommentarene inneholder krav til de ulike delene, som du må ta hensyn til når du løser oppgavene. I tillegg til metodene som er oppgitt, står du fritt til å definere ekstra metoder for å gjøre løsningen ryddigere. Nyttige standardklasser og -metoder finnes i vedlegg 3.

Temaet for oppgaven er et spisested (Diner) og problemstillingen er plassering (Seating) av grupper (Group) av gjester ved bordene (Table).

Group-, Table- og Seating-klassene (vedlegg 1) er såkalte verdi-klasser, med data som skal oppgis ved opprettelsen av objektene og siden ikke skal kunne endres. Group skal inneholde data om antall gjester i gruppa, Table skal inneholde data om antall stoler (capacity) og Seating skal holde rede på bordet en gitt gruppe sitter på.

Oppgave a)

Skriv ferdig Group og Seating, inkludert nødvendige innkapslingsmetoder. 

public class Group {
 
       private final int guestCount;
 
       public Group(int guestCount) {
              this.guestCount = guestCount;
       }
 
       public int getGuestCount() {
              return guestCount;
       }
}
 
public class Seating {
 
       private final Table table;
       private final Group group;
 
       public Seating(Table table, Group group) {
              this.table = table;
              this.group = group;
       }
      
       public Table getTable() {
              return table;
       }
      
       public Group getGroup() {
              return group;
       }
}
Oppgave b)

En skal ikke kunne ha Seating-objekter for bord som ikke har mange nok stoler til hele gruppa som er plassert der. Skriv koden som trengs for å sikre at denne regelen overholdes.

Følgende kode legges øverst i konstruktøren:

if (table.getCapacity() < group.getGuestCount()) {
   throw new IllegalArgumentException("The table is too small for the group");
}

Viktig å utløse unntak, ikke bare en if rundt initialiseringskoden.

Oppgave c)

Anta at Group hadde en metode for å endre antall gjester. Forklar med tekst og/eller kode hvilke endringer du måtte gjort for å sikre at regelen i b) overholdes.

Group måtte hatt en referanse til Seating-objektet (eller Table-objektet) som ble satt av Seating, så den kunne kjøre tilsvarende sjekk som over, ved endring av størrelsen. Uten en slik referanse hjelper det ikke å si at en skal sjekke den nye gruppestørrelsen opp mot antall stoler ved bordet. En kan til nød bruk observatør-observert-teknikken, men det er overkill her.

 

Oppgave d)

I tillegg til antall stoler, skal et bord ha et bordnummer. Dette skal være et unikt løpenummer som ikke oppgis, men settes automatisk av kode i Table-klassen selv når Table-objekter opprettes. Det aller første bordet som lages skal få 1 som nummer, det andre skal få 2 osv. Implementer konstruktøren og annen nødvendig kode, inkludert getNum-metoden!

Her er poenget at en trenger en global teller, som en får til i Java ved bruk av static. Denne må brukes og økes i Table sin konstruktør.

private static int tableCounter = 1; 
 
private final int num;
private final int capacity;
 
public Table(int capacity) {
       this.num = tableCounter++;
       this.capacity = capacity;
}

Diner-klassen (vedlegg 1) holder rede på bord (tables) og bordplasseringer (seatings), altså hvilke grupper som sitter ved hvilke bord.

Oppgave a)

Skriv nødvendige felt-deklarasjoner og konstruktør(er), gitt at spisestedet har mer enn ett bord. Skriv også metodene for å legge til og fjerne bord.

Vi velger Collection, fordi vi ikke trenger indeksbasert tilgang til elementene. Vi ser ikke noe behov for en konstruktør, siden en like gjerne kan legge til bord ved kall til addTable. Det er greit nok å ha en konstruktør som tar inn et sett bord, f.eks. en varargs som i Table... tables, og da er det viktig å initialisere lokale variabler slik at de ikke kan endres utenifra. Merk at Arrays.asList(...) lager en List som ikke kan endre størrelse, så den kan ikke bruke direkte som verdien til et lokal tables-felt. Hvis en kan gi inn Seating-objekter, så må de valideres, slik at en ikke kan omgå valideringen som vanligvis gjøres i createSeating/addSeating.

Det vil være mulig å bruke én tableSeatings-Map/HashMap i stedet for to lister, hvor tableSeatings.keySet() tilsvarer tables og tableSeatings.values() tilsvarer seatings.

private Collection<Table> tables = new ArrayList<>(); 
private Collection<Seating> seatings = new ArrayList<>();
 
public void addTable(Table table) {
 tables.add(table);
}
 
public void removeTable(Table table) {
 if (isOccupied(table)) {
 throw new IllegalArgumentException("Cannot remove an occupied table");
 }
 tables.remove(table); 
}

 

 

Oppgave b)

Skriv metodene isOccupied og getCapacity.

public boolean isOccupied(Table table) {
for (Seating seating : seatings) {
              if (seating.getTable() == table) {
                      return true;
              }
       }
       return false;
       // return seatings.stream().anyMatch(s -> s.getTable() == table);
}
      
public int getCapacity(boolean includeOccupied) {
       int capacity = 0;
       for (Table table : tables) {
              if (includeOccupied || (! isOccupied(table))) {
                      capacity += table.getCapacity();
              }
       }
       return capacity;
       // Stream<Table> stream = tables.stream();
       // if (! includeOccupied) {
       //     stream = stream.filter(t -> ! isOccupied(t));
       // }
       // return stream.mapToInt(Table::getCapacity).sum();
}
Oppgave c)

Bord kan settes samme, typisk for å få plass til store grupper med gjester. Tilsvarende kan bord deles opp, for å unngå at en liten gruppe tar opp et stort bord. Skriv metodene mergeTables og splitTable. I denne omgang skal det ikke registreres hvilke bord som faktisk settes sammen, de forsvinner bare, og må opprettes på nytt ved oppdeling.

isOccupied-sjekken kan virke unødvendig, siden den også gjøres i removeTable, men det er best å sjekke på forhånd og ikke underveis, så en ikke utfører endringene delvis (uten å reversere endringene).

public void mergeTables(Table table1, Table table2, int lostCapacity) {
       checkNotOccupied(table1);
       checkNotOccupied(table2);
       Table table = new Table(table1.getCapacity() + table2.getCapacity() - lostCapacity);
       removeTable(table1);
       removeTable(table2);
       addTable(table);
}
      
public void splitTable(Table table, int capacity1, int capacity2) {
       checkNotOccupied(table);
       removeTable(table);
       addTable(new Table(capacity1));
       addTable(new Table(capacity2));
}
 
void checkNotOccupied(Table table) {
       if (isOccupied(table)) {
              throw new IllegalArgumentException("The table cannot be occupied");
       }
}
Oppgave d)

Tegn et objekttilstandsdiagram som illustrerer virkemåten til mergeTables.

Den første tilstanden kan inneholde Diner-objekt koblet til to Table-objekter. Transisjonen er et kall til mergeTables. Den andre tilstanden inneholder det samme Diner-objektet koblet til et nytt Table-objekt. capacity-feltene og lostCapacity-argumentet må stemme overens.Welcome to PlantUML! You can start with a simple UML Diagram like: Bob->Alice: Hello Or class Example You will find more information about PlantUML syntax onhttps://plantuml.com (Details by typinglicensekeyword) PlantUML 1.2024.4 <b>This version of PlantUML is 102 days old, so you should<b>consider upgrading from https://plantuml.com/download[From string (line 2) ] @startumlobject "~#diner : Diner" as diner1 { } object "~#table1 : Table" as table1 { num = 1 capacity = 4 } object "~#table2 : T ...Syntax Error?

Oppgave e)

Når gjester skal plasseres må en finne det minste, ledige bordet med nok kapasitet. Skriv metodene hasCapacity og findAvailableTables. Skriv også annen nødvendig kode for å tilfredsstille kravet om sortering av returverdien til findAvailableTables.

For hasCapacity er det viktig å ha med en sjekk for om bordet er opptatt, ikke bare sjekke bordets totale kapasitet. For findAvailableTables så er litt av poenget å skjønne at selv om en Collection ikke alltid har en ordning og kan sorteres, så kan en lage or returnere en List som er ordnet og sortert.

public boolean hasCapacity(Table table, int capacity) {
       return (! isOccupied(table)) && table.getCapacity() >= capacity;
}
 
public Collection<Table> findAvailableTables(int capacity) {
       List<Table> result = new ArrayList<>();
       for (Table table : tables) {
              if (hasCapacity(table, capacity)) {
                      result.add(table);
              }
       }
       Collections.sort(result);
       return result;
}

Table må implementere Comparable<Table> for at sort-metoden skal kunne brukes og virke. Alternativt kan man lage en (implementasjon av) Comparator<Table>, f.eks. med (t1,t2) -> t1.getCapacity() – t2.other.getCapacity().

@Override
public int compareTo(Table other) {
       return getCapacity() - other.getCapacity();
}
Oppgave f)

En ny bordplassering registreres i et Seating-objekt. Skriv metodene createSeating, addSeating og removeSeating.

Her er litt av poenget hvordan en skal håndtere at resultatet fra findAvailableTables er en Collection, som ikke har en ”opplagt” metode for å hente ut første element. Her brukes iterator() og ett kall til next(). Kunne brukt iterator() først og så hasNext() for å sjekke om det er elementer å hente, i stedet for isEmpty (se utkommentert kode). Det er ikke bra å caste til List, selv om en vet at det er en List i praksis, eller endre på retur-typen til findAvailableTables.

 

Det er ikke meningen å utløse unntak i createSeating, hvis ingen bord er ledige, men hvis det gjøres så må addSeating kodes med try/catch og riktig retur-verdi.

public Seating createSeating(Group group) {
       Collection<Table> availableTables = findAvailableTables(group.getGuestCount());
       if (availableTables.isEmpty()) {
              return null;
       }
       return new Seating(availableTables.iterator().next(), group);
//     Iterator<Table> tablesIt = availableTables.iterator();
//     return (tablesIt.hasNext() ? new Seating(tablesIt.next(), group) : null;
}
 
public boolean addSeating(Group group) {
       Seating seating = createSeating(group);
       if (seating != null) {
              seatings.add(seating);
              return true;
       }
       return false;
}
 
public void removeSeating(int tableNum) {
       for (Seating seating : seatings) {
              if (seating.getTable().getNum() == tableNum) {
                      seatings.remove(seating);
                      return; // eller break
              }
       }
}

 

.

Oppgave a)

.

 

.

Oppgave b)

.

.

 

Oppgave a)

 

Oppgave b)

.

.
Oppgave c)

.

.
Oppgave a)

 

Oppgave b)

.

.
Oppgave c)

.

.
/**
 * This class ...
 */

 

 

  • No labels