...
Expand | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Denne første versjonen av systemet består av tre klasser. Patient er en tom klasse. Doctor-klassen har en assosiasjon til en Patient. TreatmentUnit holder rede på hvilke pasienter og doktorer som finnes, og styrer avvikling av behandlingskøen. Pasienter som har blitt behandlet av en lege fjernes fra systemet. Pasientene har altså ingen (helse)tilstander her.
Oppgave a)Skriv ferdig Doctor-klassen i henhold til skjelettet, altså nødvendige innkapslingsmetoder og isAvailable. Patient er så langt en tom klasse, du trenger ikke implementere denne.
Oppgave b)Skriv følgende deler av klassen TreatmentUnit, basert på beskrivelsen i skjelettet:
Vær obs på at enkelte av disse metodene bør kalle startTreatment fra 1c.
Oppgave c) - TreatmentUnit: Koble pasient og doktorHver gang en ny pasient eller lege er lagt til, eller en lege har avsluttet en behandling, bør TreatmentUnit forsøke å koble en ledig lege og en pasient som skal behandles. Implementer de to startTreatment-metodene og treatmentFinished (sistnevnte brukes ikke i denne underoppgaven, men senere).
|
Expand | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Alle pasienter har på forhånd blitt diagnostisert med en eller flere (helse)tilstander (conditions) som må behandles. Tilsvarende har alle doktorer et sett med tilstander som de er kompetente til å behandle. En doktor kan ikke behandle pasienter den ikke har kompetanse til å behandle, og en pasient må være i systemet helt til alle tilstander er behandlet.
Oppgave 2a) - PatientImplementer følgende deler av Patient-klassen i henhold til skjelettet:
Oppgave 2b) - DoctorImplementer følgende deler av Doctor-klassen i henhold til skjelettet:
Oppgave 2c - TreatmentUnitNå som pasienter har ulike tilstander, og doktorer kan behandle slike tilstander, må dette taes hensyn til i klassen TreatmentUnit. En doktor kan ikke behandle pasienter den ikke har kompetanse til å behandle, og en pasient må være i systemet helt til alle tilstander er behandlet.
Oppgave 2d - Testing Du har fått utdelt et skjelett med halvferdige testmetoder (TreatmentUnitTest). Gjør testmetodene fullstendige i henhold til kommentarene. Du vil finne dokumentasjon av testing i vedlegget nederst på siden.
Oppgave 2e) - SekvensdiagramTegn sekvensdiagram av det som skjer mellom start sequence diagram- og end sequence diagram-kommentarene i testklassen i skjelettkoden. Diagrammet skal inkludere testen selv, akuttmottaket, pasienten og doktoren som (i den delen av testen) deltar i behandlingen. Du skal ikke ha med kode du legger til selv (f.eks. kall til assert-metoder), som svar på 2 d). Det skulle være omtrent 8-10 kall til metoder i denne delen.
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Expand | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Expand | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Du har fått utdelt et skjelett med halvferdige testmetoder (TreatmentUnitTest). Gjør testmetodene fullstendige i henhold til kommentarene. Du vil finne dokumentasjon av testing i vedlegget nederst på siden.
Oppgave 2e) - SekvensdiagramTegn sekvensdiagram av det som skjer mellom start sequence diagram- og end sequence diagram-kommentarene i testklassen i skjelettkoden. Diagrammet skal inkludere testen selv, akuttmottaket, pasienten og doktoren som (i den delen av testen) deltar i behandlingen. Du skal ikke ha med kode du legger til selv (f.eks. kall til assert-metoder), som svar på 2 d). Det skulle være omtrent 8-10 kall til metoder i denne delen.
|
Expand | ||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||||||||||||
Fokus i del 3 er organisering av objektorientert kode med arv. Oppgaven kan derfor løses ved hjelp av pseudokode. Systemet skal støtte tre ulike logikker for å knytte doktor til pasient:
I denne deloppgaven skal du beskrive eller implementere støtte for disse egenskapene ved å bruke arv. Du finner ikke noe nytt skjelett til denne oppgaven, men du vil finne skjelettet fra del 2 for oppslag. Vi ønsker å ende opp med tre klasser, TreatmentUnit, PriorityTreatmentUnit og EmergencyPriorityTreatmentUnit, som implementerer hver sin logikk i lista over. Forskjellen dem i mellom skal være hvordan de implementerer startTreatment-metodene. De skal være knyttet sammen med arv for å gjøre det enkelt for andre klasser å bytte mellom dem og for å gi god gjenbruk, men detaljene i hvordan arvingsmekanismen brukes skal være opp til deg. Det er lov å innføre ekstra klasser og metoder i arvingshierarkiet, hvis det gjør løsningen bedre. Forklar med tekst og kode hvordan du vil 1) strukturere arvingshierarkiet og 2) hvilke metoder som deklareres/implementeres hvor. Skriv også kode for metodene. Siden fokuset her er mer på objektorientert organisering av kode, vil det også gis poeng for pseudokode.
|
Expand | ||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||||||||||||
Mens du i del 3 brukte arv, skal du i denne delen bruke delegering for å oppnå det samme. I henhold til delegeringsteknikken definerer vi et grensesnitt, DoctorAllocator. I tillegg lager du (minst) tre hjelpeklasser tilsvarende de tre logikkene beskrevet i del 3, som implementerer grensesnittet. Forklar hvordan koden i vedlagte TreatmentUnit-skjelett skal gjøres fullstendig slik at startTreatment-metoden bruker delegering riktig. Forklar også med tekst og/eller kode hvordan du vil utforme hjelpeklassene som implementerer DoctorAllocator. Da målet er å vise kunnskap om delegering kan dere bruke pseudokode i denne oppgaven. Det er greit å referere (pseudo)koden i del 3, hvis det er til hjelp.
|
Expand | ||
---|---|---|
| ||
TreatmentUnit: Løsningsforslaget viser utvidelse med en abstrakt klasse AbstractTreatmentUnit. Denne klassen implementerer alle metoder som ikke involverte regelsett for hvordan en skulle start behandling, og deklarerer de relevante abstrakte metoden (startTreatment *2). Disse ble så implementert i TreatmentUnit, med løsning akkurat som i del 2. En slik abstrakt klasse er ikke påkrevd, men en god idé, fordi den tydeliggjør hva som er ment å variere (polymorfi) i subklassene.
PriorityTreatmentUnit: Her skulle en velge ut den ventende pasienten som hadde høyest prioritet (getPriority). Selv om getPriority ikke ble oppgitt kunne en gjøre antakelser om bruk. Den eneste metoden som måtte implementeres i denne klassen var startTreatment(Doctor), siden prioritering må gjøres hver gang det oppstår en situasjon der pasientene konkurrerer om en doktor som er ankommet eller blitt ledig. Forskjellen fra tidligere implementasjon er at en må gå igjennom alle pasienter (som denne doktoren kan behandle) og lagre den pasienten som til nå har høyest prioritet. Vi har også vist pseudokode som vil være en fullgod besvarelse for denne klassen.
EmergencyPriorityTreatmentUnit: Den eneste metoden som trenger ny logikk er startTreatment(Patient). Derfor er det lurt (men strengt tatt ikke nødvendig) å arve fra PriorityTreatmentUnit, siden vi da kan utnytte den eksisterende prioriteringslogikken. Det er jo bare når en ikke finner en doktor med super.startTreatment(patient) at ny logikk for å avbryte pågående behandling er aktuell. Hvis dette er tilfelle, må en gå igjennom alle doktorer (trenger ny hjelpemetode getAllDoctors-metode) og sjekke om denne doktoren kan behandle pasienten. Merk at vi utelater alle ledige doktorer, siden disse allerede er sjekket i kallet til superklassens metode (dette kan forsåvidt skje i den nye hjelpemetoden, som da kanskje bør hete getAllTreatingDoctors). Vi bruker samme logikk som tidligere for å velge ut den lavest prioriterte pasienten. Vi oppdatere dennes nåværende doktor sin pasient til den nye pasienten (getDoctor(patientToSuspend).setPatient(patient)). Til slutt må pasienten som fikk avbrutt behandlingen få en ny sjanse til behandling av en annen lege med et nytt kall til startTreatment. Det er ganske mange ting å huske på, men det viktigste er tross alt god bruk av arv.
Også her tillates pseudokode, se eksempel i LF. Merk at oppgaven beskrev klassen PriorityTreatment, mens LF kaller den PriorityTreatmentUnit. Det er selvsagt ikke viktig.
public class PriorityTreatmentUnit extends TreatmentUnit {
protected double getPriority(Patient patient) { return 0.0; }
@Override protected boolean startTreatment(Doctor doctor) { Patient patient = null; for (Patient patient2 : getWaitingPatients()) { if (doctor.canTreat(patient2) > 0.0 && (patient == null || getPriority(patient2) > getPriority(patient))) { patient = patient2; } } if (patient != null) { doctor.setPatient(patient); return true; } return false; } }
/* PSEUDOKODE-eksempel FOR startTreatment(doctor) * Går gjennom alle pasienter som venter (getWaitingPatients()) og * holder rede på den med høyest prioritet (getPriority). * Sjekker om det finnes en doktor * med riktig kompetanse (canTreat) * Hvis man har funnet en pasient som kan behandles, * så får doktoren denne (setPatient) og true returneres. */
public class EmergencyPriorityTreatmentUnit extends PriorityTreatmentUnit {
@Override public boolean startTreatment(Patient patient) { if (! super.startTreatment(patient)) { Patient patientToSuspend = null; for (Doctor doctor : getAllDoctors()) { if (doctor.canTreat(patient) > 0.0) { Patient existingPatient = doctor.getPatient(); if (existingPatient != null && getPriority(existingPatient) < getPriority(patient) && (patientToSuspend == null || getPriority(existingPatient) < getPriority(patientToSuspend))) { patientToSuspend = existingPatient; } } } if (patientToSuspend != null) { getDoctor(patientToSuspend).setPatient(patient); startTreatment(patientToSuspend); return true; } } return false; } } |
title | Del 4 - delegering (15%) |
---|
Mens du i del 3 brukte arv, skal du i denne delen bruke delegering for å oppnå det samme. I henhold til delegeringsteknikken definerer vi et grensesnitt, DoctorAllocator. I tillegg lager du (minst) tre hjelpeklasser tilsvarende de tre logikkene beskrevet i del 3, som implementerer grensesnittet. Forklar hvordan koden i vedlagte TreatmentUnit-skjelett skal gjøres fullstendig slik at startTreatment-metoden bruker delegering riktig. Forklar også med tekst og/eller kode hvordan du vil utforme hjelpeklassene som implementerer DoctorAllocator.
Da målet er å vise kunnskap om delegering kan dere bruke pseudokode i denne oppgaven. Det er greit å referere (pseudo)koden i del 3, hvis det er til hjelp.
Expand | ||
---|---|---|
| ||
Expand | ||
| ||
LF lager løsningen rundt det oppgitte grensesnittet DoctorAllocator, som har to metoder allocateDoctor og allocatePatient. Dette grensesnittet implementeres av en DefaultDoctorAllocator (noe som tilsvarer slik det gjøres i en vanlig TreatmentUnit, altså ikke noe bruk av prioritering). De andre to proriteringsstrategiene implementeres av PriorityDoctorAllocator og EmergencyPriorityDoctorAllocator. LF bruker arv for begge disse, tilsvarende løsningen i del 3, men det er ikke viktig fordi temaet her er delegering. Logikken som implementeres er omtrent som i del 3, med en vesentlig forskjell: Grensesnitt-metodene skal bare finne og returnere riktig doktor/pasient (koblingen som skal etableres), ikke endre noe, altså kalles ikke setPatient eller startTreatment.
DefaultDoctorAllocator: Implementerer ingen prioritet og tilsvarer et vanlig TreatmentUnit-valg. Hvis doktor-pasient-kobling kan etableres returneres doktor eller pasient, ellers null.
PriorityDoctorAllocator: Arver fra DefaultDoctorAllocator, så en slipper unna med redefinering av allocatePatient, med logikk tilsvarende PriorityTreatmentUnit.
EmergencyPriorityDoctorAllocator: Arver fra PriorityDoctorAllocator, så en slipper unna med redefinering av allocateDoctor, med logikk tilsvarende EmergencyPriorityTreatmentUnit.
TreatmentUnit: Denne må endres så den 1) bruker delegering ved å kalle en DoctorAllocator-implementasjon og 2) håndterer faktisk endring av doktor-pasient-koblingen, iht. resultatet fra DoctorAllocator-implementasjonen. Delegering rigges ved å opprette en doctorAllocator-variabel, her er DefaultDoctorAllocator valgt, men den kan byttes ut med et kall til setDoctorAllocator. Begge startTreatment-metodene må endres. For begge består endringen av at en må kalle den tilsvarende allocate-metoden og sjekke om resultatet er null før en knytter relasjonen. Merk at en må ta høyde for at behandling kan avbrytes.
public interface DoctorAllocator {
/** // Long description at git */ public Patient allocatePatient(Doctor doctor, TreatmentUnit treatmentUnit);
/** // Long description at git */ public Doctor allocateDoctor(Patient patient, TreatmentUnit treatmentUnit); }
public class DefaultDoctorAllocator implements DoctorAllocator {
@Override public Patient allocatePatient(final Doctor doctor, final TreatmentUnit treatmentUnit) { for (final Patient patient : treatmentUnit.getWaitingPatients()) { if (doctor.canTreat(patient) > 0.0) { return patient; } } return null; }
@Override public Doctor allocateDoctor(final Patient patient, final TreatmentUnit treatmentUnit) { for (final Doctor doctor : treatmentUnit.getAvailableDoctors()) { if (doctor.canTreat(patient) > 0.0) { return doctor; } } return null; } }
public class PriorityDoctorAllocator extends DefaultDoctorAllocator {
/** * Dummy class */ protected double getPriority(final Patient patient) {
return 0.0; }
@Override public Patient allocatePatient(final Doctor doctor, final TreatmentUnit treatmentUnit) { Patient patient = null; for (final Patient patient2 : treatmentUnit.getWaitingPatients()) { if (doctor.canTreat(patient2) > 0.0 && (patient == null || getPriority(patient2) > getPriority(patient))) {
patient = patient2; } } return patient; } }
public class EmergencyPriorityDoctorAllocator extends PriorityDoctorAllocator {
@Override public Doctor allocateDoctor(final Patient patient, final TreatmentUnit treatmentUnit) { final Doctor doctor = super.allocateDoctor(patient, treatmentUnit); if (doctor != null) { return doctor; } Patient patientToSuspend = null; for (final Doctor doctor2 : treatmentUnit.getAllDoctors()) { if (doctor2.canTreat(patient) > 0.0) { final Patient existingPatient = doctor2.getPatient(); if (existingPatient != null && getPriority(existingPatient) < getPriority(patient) && (patientToSuspend == null || getPriority(existingPatient) < getPriority(patientToSuspend))) { patientToSuspend = existingPatient; } } } if (patientToSuspend != null) { return treatmentUnit.getDoctor(patientToSuspend); } return null; } } |
Vi gjentar fra del 3 hvordan vi ønsker å knytte doktor til pasient, men dette skal nå gjøres ved å implementere DoctorAllocator i stedet for å bruke arv:
- Pasienter knyttes til den første (og ikke nødvendigvis beste) doktoren som kan behandle en eller flere tilstander hos pasienten.
- Pasientene kan ha tilstander med varierende kritikalitet, som gjør at de prioriteres foran resten av køen når en doktor er ledig. Du kan forutsette at det finnes en getPriority-metode i Patient, og at denne prioriteten settes ved innlegging av pasientdata. Per, som har en mer alvorlig skade enn Jens, vil altså behandles raskere selv om han kommer inn senere. (Såfremt Jens ikke allerede har startet behandlingen.)
- Pågående pasientbehandling (selv om den er prioritert) skal kunne avbrytes og doktoren skal starte behandling av en ny pasient. Dette skal skje dersom det kommer inn en ny pasient, og dennes prioritet er høyere enn hos pågående. Du kan også her forutsette at den finnes en getPriority-metode i Patient. Anta at Jens er under behandling hos doktor Who. Ida kommer har en tilstand med høyere prioritet enn Jens, og kan kun behandles av Who. Jens sin behandling blir derfor satt på vent, og den samme doktoren begynne behandling av Ida.
...
Expand | ||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||||||||||||||||||
På venterommet skal det være et informasjonspanel, som forteller ventende pasienter om hvilken doktor de skal gå til, når det er deres tur. Panelet implementeres med JavaFX, og tanken er at kontroller-objektet (av typen TreatmentUnitController ) skal lytte på et TreatmentUnit-objekt og få beskjed når koblingen mellom doktor og pasient etableres, slik at panelet kan vise en tekst av typen «Pasient X skal gå til Doctor Y» (du kan forvente at Patient og Doktor har en toString() som gir navn, dette trenger du ikke implementere i tidligere oppgaver). I denne delen kan du bygge videre på koden fra del 2. Du trenger altså ikke ha løst del 3 eller 4.
Oppgave 5a) – Lyttergrensesnitt
Oppgave 5b) – Controller Fyll ut kodeskjelettet for kontrollerklassen TreatmentUnitController, slik at det fungerer med den vedlagte FXML-koden og fyller rollen som observatør av TreatmentUnit. Som indikert i kodeskjelettet, så kan du anta at treatmentUnit-variablen i kontrollerklassen settes «automagisk» i konstruktøren.
|
...