Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Expand
titleDel 2 - andre trinn av Doctor og Patient, samt testing. (30%)

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.

Expand
titleSkjelett del 2


Code Block
languagejava
/** * A patient has a set of (health) conditions (of type String) that needs to be treated.
 * Supports iterating over the patient's conditions.
 */
public class Patient {


       // Add fields, constructors, and methods here: // 2a
        // Support iteration // 2a
     /**
    * Indicates if this patient has conditions that needs to be treated.
    * @return true if this patient has conditions that needs to be treated, 
    * false otherwise.
    */
       public boolean requiresTreatment() { // 2a
      ...
   }
}


/**
 * A doctor has the capacity to treat one patient at a time.
 * The doctor as a list of competencies (of type String) that
 * indicates what conditions s/he can treat.
 */
public class Doctor {


       // Internal variables go here // 2b


       /**
    * Initialise this doctor with a set of competencies.
    * @param competencies
    */
       public Doctor(...) { // 2b
     ... 
   }
       
       /**
     * Indicates to what extent this doctor can treat the provided patient.
     * The value is the number of the patient's conditions this doctor 
     * can treat divided by the number of conditions the patient has.
     * Conditions and competences are matched using simple String comparison.
     * @param patient
     * @return the ratio of the patient's conditions that this 
     * doctor can treat.
     */
       public double canTreat(final Patient patient) { // 2b
       ...
   }


       /**
    * "Treats" the patient by removing all the patient's conditions 
    * that this doctor can treat.
    */
       public void treat() { // 2b
        ...
   }


       /**
    * @return the patient this doctor is treating, or null if s/he 
    * isn't currently treating any patient.
    */
       public Patient getPatient() {
             // Implementation hidden
   }


       /**
    * @return true if this doctor is currently treating a patient, otherwise 
    * false.
    */
       public boolean isAvailable() {
             // Implementation hidden
   }


       /**
    * Sets the patient that this doctor is treating, use null to indicate
    * s/he isn't currently treating any patient.
    * @param patient
    */
       public void setPatient(final Patient patient) {
              // Implementation hidden
   }
}




/**
 * A class for managing a set of doctors and the patients they're treating.
 * When doctors or patients arrive, it is made sure that patients are treated 
 * as soon as possible.
 */
public class TreatmentUnit {


       // Internal declaration hidden
       
       /**
    * Adds a doctor and makes sure s/he starts treating a patient, if one 
    * is waiting.
    * @param doctor
    */
       public void addDoctor(final Doctor doctor) {
             // Possible changes
   }


       /**
    * @return the currently available doctors
    */
       public Collection<Doctor> getAvailableDoctors() {
             // Possible changes
      }


       /**
    * Adds a patient to this treatment unit, and makes sure treatment starts
    * if any doctor is available.
    * Otherwise the patient is queued for treatment when a doctor becomes
    * available.
    * @param patient
    */
       public void addPatient(final Patient patient) {
           // Possible changes
   }


       /**
    * @param pred the predicate that the doctor must satisfy
    * @return some doctor satisfying the predicate
    */
       public Doctor getDoctor(final Predicate<Doctor> pred) {
          // Possible changes
   }


       /**
    * Find the doctor, if any, that treats the provided patient.
    * @param patient
    * @return the doctor treating the provided patient, or null if the 
    * patient isn't currently being treated.
    */
       public Doctor getDoctor(final Patient patient) {
          // Possible changes
   }


       /**
    * Find all patients that are not currently being treated
    * @return the patients not currently being treated
    */
       public Collection<Patient> getWaitingPatients() {
             // Possible changes
   }


       /**
    * Finds a waiting patient and sets him/her as the provided doctor's patient.
    * Will only accept a patient that has some condition that the doctor actually 
    * can treat.
    * @param doctor the doctor for which a patient to treat should be found
    * @return true if a patient for the provided doctor was found, false 
    * otherwise.
    */
       private boolean startTreatment(final Doctor doctor) {
             // Possible changes
   }


       /**
    * Finds an available doctor for the provided patient, and sets that 
    * doctor to treat the patient.
    * Will only accept a doctor that actually can treat some condition for the 
    * provided patient.
    * @param patient the patient for which a treating doctor should be found
    * @return true if a doctor for the provided patient was found, false 
    * otherwise.
    */
       private boolean startTreatment(final Patient patient) {
             // Possible changes
   }


       /**
    * Removes the link between doctor and patient, after treatment is finished.
    * If the patient is fully treated, s/he is removed from this treatment unit,
    * otherwise another round of treatment is initiated.
    * Also ensure the doctor starts treating another patient.
    * @param doctor the doctor that has finished treating his/her patient
    */
       public void treatmentFinished(final Doctor doctor) {
             // Possible changes
   }
}


/**
 * Used to test TreatmentUnit
 */
public class TreatmentUnitTest {


       private TreatmentUnit treatmentUnit;


       @Before
       public void setUp() {
             ...
   }




       @Test
       public void testAddDoctorsPatient() {
          final Doctor doctor1 = new Doctor(...); // new doctor can treat "flu"
          final Doctor doctor2 = new Doctor(); // new doctor can treat "noseblead" and "pneumonia"
       treatmentUnit.addDoctor(doctor1);
       treatmentUnit.addDoctor(doctor2);
          // Test that both doctors are available.
       ...
             
           final Patient patient = new Patient();
        patient.addConditions(...); // patient has conditions "flu" and "noseblead"
           // 2e) start sequence diagram  
        treatmentUnit.addPatient(patient);
           // Test that only one of the doctors are available:
        ...
        Doctor patientDoctor = treatmentUnit.getDoctor(patient);
        patientDoctor.treat();
        treatmentUnit.treatmentFinished(patientDoctor);
     // 2e) end sequence diagram
           // Test that the previous doctor is available and that a
     // new doctor has been assigned to the patient.
         ...
             
         patientDoctor = treatmentUnit.getDoctor(patient);
         patientDoctor.treat();
          treatmentUnit.treatmentFinished(patientDoctor);
             // Test that both doctors are available:
             ...
   }
}

 


Oppgave 2a) - Patient

Implementer følgende deler av Patient-klassen i henhold til skjelettet:

  • Bestem selv hvilke felt, konstruktører og metoder du trenger for håndtering av(helse)tilstander (conditions).
  • Begrunn valget av løsningen, spesielt hvilke typer du har valgt å bruke.
  • requiresTreatment: returnerer om pasienten trenger behandling.
  • Legg i tillegg inn støtte som lar andre klasser iterere over pasientens tilstander, på enklest mulig måte.
  • VIKTIG: Merk at koden du skriver i oppgave 2b og den oppgitte testkoden i oppgave 2d skalpasse til dine valg.
Expand
titleLF

 

Vi velger igjen Collection, fordi vi ikke trenger indeksbasert tilgang til conditions. Det kan derimot også gjerne brukes et sett (Set-grensesnittet og HashSet-implementasjonsklassen).

Vi har valgt å ha en tom konstruktør og en addConditions-metoden, en kunne gjerne hatt en konstruktør som tar inn tilstander i stedet, siden en jo ikke trenger å legge til tilstander underveis i behandlingsforløpet, bare fjerne dem (removeConditions).

addConditions(…): Denne skal ta inn en samling, og vi har valgt å bruke varargs, i stedet for å ta inn en Collection.

requiresTreatment(): Pasienten trenger behandling dersom conditions ikke er tom.

removeConditions(…): Denne fjerner de angitte tilstandene, og vi valgte å ta inn en Collection, da det er praktisk både der den brukes og for implementasjonen.

Implements Iterable og iterator()-metoden: Disse gir støtte for å iterere over tilstandene til en pasient, og hente ut en og en tilstand, for eksempel i en for-løkke. Dette ivaretar innkapsling, siden den underliggende lista ikke eksponeres (mer enn nødvendig).

Code Block
languagejava
public class Patient implements Iterable<String> {
 
               private Collection<String> conditions = new ArrayList<>(); // 2a
 
               public void addConditions(String... conditions) { // 2a
                    this.conditions.addAll(Arrays.asList(conditions));
               }
 
               public boolean requiresTreatment( ) { // 2a
                              return ! conditions.isEmpty();
               }
 
               public void removeConditions(Collection<String> conditions) {  // 2a
                              this.conditions.removeAll(conditions);
               }
 
               @Override
               public Iterator<String> iterator() { // 2a
                              return conditions.iterator();
               }
}


Oppgave 2b) - Doctor

Implementer følgende deler av Doctor-klassen i henhold til skjelettet:

  • Avgjør intern lagring av tilstander legen kan behandle. Du trenger ikke argumentere for valget.
  • Konstruktør: Tenk spesielt på innkapsling av data for å hindre endring fra utenfor klassen. Velg selv parameterliste.
  • canTreat: For en gitt pasient skal metoden returnere andelen av dennes tilstander som doktoren kan behandle. Hvis Jens har fem tilstander, og Dr. Who kan behandle tre av dem, så skal metoden returne tallet 0.6.
  • treat: For en gitt pasient fjernes de tilstandene som doktoren kan behandle.
Expand
titleLF
 

Intern lagring av tilstander en doktor kan behandle håndteres greit med en Collection, List er unødvendig jamfør samme problemstilling som i Patient.

Konstruktør: Den skal håndtere et sett med tilstander doktoren kan kurere, som er av typen String. Her er det enda mer naturlig enn for Patient å bruke konstruktøren til å sette tilstandene, siden det i hvert fall er lite behov for å endre kompetansen til doktoren i løpet av et behandlingsforløpet. Merk at Arrays.asList gir en Collection som ikke kan endre størrelse.

canTreat(): Gå gjennom alle tilstandene pasienten har (utnytter at Patient implementerer Iterable), og teller dem og hvilke av dem denne doktoren kan behandle. Merk hvordan telleren må gjøres om til double før divisjonen, så resultatet ikke avkortes til 0.

treat(): Denne metoden gjelder pasienten som er under behandling (satt med setPatient) og fjerner alle tilstander hos pasienten som denne doktoren kan behandle. Dette krever en egnet removeConditions-metode i Patient, detaljene kan variere.


Code Block
languagejava
public class Doctor {
 
               private Collection<String> competencies; // 2b
 
               public Doctor(String... competencies) { // 2b
                              this.competencies = Arrays.asList(competencies);
               }
 
               public double canTreat(Patient patient) {
                              int count = 0, total = 0;
                              for (String item : patient) {
                                            total++;
                                            if (competencies.contains(item)) {
                                                           count++;
                                            }
                              }
                              return ((double) count) / total;
               }
 
               public void treat() {
                              patient.removeConditions(competencies);
               }
}


Oppgave 2c - TreatmentUnit

Nå 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.

  • Implementer endring i TreatmentUnit i relevante metoder. Du trenger ikke kopiere inn metoder som ikke endrer seg fra del 1, du skal heller ikke endre svar i del 1.
Expand
titleLF
 

De eneste metodene som blir påvirket, og dermed må endres, er de to startTreatment-metodene og treatmentFinished. Her må en passe på å respektere dokumentasjonen når det gjelder returverdier som true og false.

startTreatment(Doctor): Som før går en gjennom alle pasientene som trenger behandling (getWaitingPatients), men nå sjekker vi også om doktoren faktisk har kompetanse til å gjøre noe med pasientens tilstander vha. et kall til doctor.canTreat(patient). Vi prøver ikke å finne pasienten med størst behandlingspotensiale, altså størst canTreat-resultat.

startTreatment(Patient): Vi går som tidligere gjennom alle de ledige doktorer (getAvailableDoctors), og som over, sjekker vi en ledig doktor har relevant kompetanse (canTreat). Her gjør vi heller ingenting for å finne beste doktor for denne pasientens tilstander.

treatmentFinished(): Forskjellen fra del 1 er at vi nå sjekker om pasienten er ferdigbehandlet eller trenger mer behandling (requiresTreatment). Hvis så er tilfelle trigger vi (forsøk på) behandling med startTreatment(patient), ellers fjernes pasienten fra pasientlista. Som før så får doktoren som er ferdig (muligens) en ny pasient vha. startTreatment(doktor).

Code Block
languagejava
               private boolean startTreatment(Doctor doctor) { // 2c
                              for (Patient patient : getWaitingPatients()) {
                                            if (doctor.canTreat(patient) > 0.0) {
                                                           doctor.setPatient(patient);
                                                           return true;
                                            }
                              }
                              return false;
               }
 
               private boolean startTreatment(Patient patient) { // 2c
                              for (Doctor doctor : getAvailableDoctors()) {
                                            if (doctor.canTreat(patient) > 0.0) {
                                                           doctor.setPatient(patient);
                                                           return true;
                                            }
                              }
                              return false;
               }
 
               public void treatmentFinished(Doctor doctor) { // 2c
                              if (doctor.getPatient() == null) {
                                            throw new IllegalStateException(doctor + " has no patient!");
                              }
                              Patient patient = doctor.getPatient();
                              doctor.setPatient(null);
                              if (patient.requiresTreatment()) {
                                            startTreatment(patient);
                              } else {
                                            patients.remove(patient);
                              }
                              startTreatment(doctor);
               }

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.

Expand
titleLF
 

LF beskriver testene på en kompakt måte, det er ingen ting i veien for å dele dem opp litt. For øvrig er det lite å bemerke ved løsningen.

 

Code Block
languagejava
public class TreatmentUnitTest {
 
               private TreatmentUnit treatmentUnit;
 
               @Before
               public void setUp() {
                              treatmentUnit = new TreatmentUnit();
               }
 
 
               @Test
               public void testAddDoctorsPatient() {
                              Doctor doctor1 = new Doctor("flu");
                              Doctor doctor2 = new Doctor("noseblead");
                              treatmentUnit.addDoctor(doctor1);
                              treatmentUnit.addDoctor(doctor2);
                              assertTrue(doctor1.isAvailable() && doctor2.isAvailable());
                              Patient patient = new Patient();
                              patient.addConditions("flu", "noseblead");
                   // 2e) start sequence diagram
                              treatmentUnit.addPatient(patient);
                              assertTrue(! (doctor1.isAvailable() && doctor2.isAvailable()));
                              Doctor patientDoctor = treatmentUnit.getDoctor(patient);
                              patientDoctor.treat();
                      treatmentUnit.treatmentFinished(patientDoctor);
                   // 2e) end sequence diagram
                              assertTrue(patientDoctor.isAvailable());
                              assertNotSame(patientDoctor, treatmentUnit.getDoctor(patient));
                              patientDoctor = treatmentUnit.getDoctor(patient);
                              patientDoctor.treat();
                      treatmentUnit.treatmentFinished(patientDoctor);
                              assertTrue(doctor1.isAvailable() && doctor2.isAvailable());
               }
}


Oppgave 2e) - Sekvensdiagram

Tegn 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
titleLF
 

Løsningsforslaget beskriver hele flyten, og inkluderer kall og objekter som oppstår som et resultat av treatmentFinished(). Kommentaren om antall kall var litt misvisende, siden det var meningen å flytte end sequence diagram-markøren én linje opp, altså over kallet til treatmentFinished(). Løsningen er derfor mer omfattende enn antydet, noe vi må ta hensyn til ved sensuren.


...

Expand
titleDel 5 – observatør-observatør-teknikken og JavaFX (15%)

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.

Expand
titleSkjelett til del 5

 


Code Block
// TreatmentUnit.fxml:<?xml version="1.0" encoding="UTF-8"?>


<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.text.Font?>


<HBox xmlns:fx="http://javafx.com/fxml/1" fx:controller="ord2019.part5.TreatmentUnitController">
            <Label fx:id="patientMessage" text="&lt;Her kommer meldinger til pasienter&gt;">
            </Label>
</HBox>




// TreatmentUnitController.java:
public class TreatmentUnitController {


            TreatmentUnit treatmentUnit;
            
            public TreatmentUnitController() {
                        treatmentUnit = // assume treatmentUnit is set automatically.
                        // ... 5 b) other initialization ...
            }


            // ... 5 b) declarations and methods here...
}


Oppgave 5a) – Lyttergrensesnitt

Definer et egnet lyttergrensesnitt og forklar med tekst og kode hvordan TreatmentUnit må endres for å kunne fungere som observert i et observatør-observert-forhold.

Expand
titleLF

 

Standardteknikken krever et passende lyttergrensesnitt og en liste av lyttere som kalles på passende sted. En bruker gjerne en Collection for å lagre lytterne og add/remove-metoder for å administrere lytterne.

LF definerer lyttergrensesnittet TreatmentListener. Dette grensesnittet har én metode, som kalles idet doktor-pasient-koblingen etableres, altså på tidspunktet det passer å gi beskjed til pasienten om hvilken doktor hen skal gå til. Metoden bør ta inn faktisk pasient, doktor og akuttmottak (TreatmentUnit).

Endringer som må gjøres i den observerte, TreatmentUnit, er at alle lytterne lagres i en private Collection av type TreatmentListener, og at den legger inn relevante public add/remove til denne. I tillegg defineres hjelpemetoden fireTreatmentStarted, som kalles rett etter at setPatient har etablert en ny doktor-pasient-kobling (i både startTreatment(Doctor) og startTreatment(Patient)).


Code Block
languagejava
public interface TreatmentListener
{

 

               public void
 {
 
               public void treatmentStarted(Doctor doctor, Patient patient, TreatmentUnit treatmentUnit);

 

}

 

public class TreatmentUnit {

 

               // Lots of code unchanged here, find it in part 2 of TU.

 

 

               private Collection<TreatmentListener> treatmentListeners = new ArrayList<>();

 

               public void addTreatmentListener(TreatmentListener listener) {

                              treatmentListeners.add(listener);

               }

 

               public void removeTreatmentListener(TreatmentListener listener) {

                              treatmentListeners.remove(listener);

               }

 

               private void fireTreatmentStarted(Doctor doctor, Patient patient) {

                              for (TreatmentListener treatmentListener : treatmentListeners) {

                                             treatmentListener.treatmentStarted(doctor, patient, this);

                              }

               }

 

               private boolean startTreatment(Doctor doctor) {

                              for (Patient patient : getWaitingPatients()) {

                                            if

 
}
 
public class TreatmentUnit {
 
               // Lots of code unchanged here, find it in part 2 of TU.
 
 
               private Collection<TreatmentListener> treatmentListeners = new ArrayList<>();
 
               public void addTreatmentListener(TreatmentListener listener) {
                              treatmentListeners.add(listener);
               }
 
               public void removeTreatmentListener(TreatmentListener listener) {
                              treatmentListeners.remove(listener);
               }
 
               private void fireTreatmentStarted(Doctor doctor, Patient patient) {
                              for (TreatmentListener treatmentListener : treatmentListeners) {
                                             treatmentListener.treatmentStarted(doctor, patient, this);
                              }
               }
 
               private boolean startTreatment(Doctor doctor) {
                              for (Patient patient : getWaitingPatients()) {
                                            if (doctor.canTreat(patient) > 0.0)
{                                                          
 {
                                                           doctor.setPatient(patient);
                                                          

                                                           fireTreatmentStarted(doctor, patient);

                                                           return true;

                                            }

                              }

                              return false;

               }

 

               private boolean startTreatment(Patient patient) {

                              for (Doctor doctor : getAvailableDoctors()) {

                                            if

                                                           return true;
                                            }
                              }
                              return false;
               }
 
               private boolean startTreatment(Patient patient) {
                              for (Doctor doctor : getAvailableDoctors()) {
                                            if (doctor.canTreat(patient) > 0.0)
{                                                          
 {
                                                           doctor.setPatient(patient);

                                                           fireTreatmentStarted(doctor, patient);

                                                           return true;

                                            }

                              }

                              return false;

               }


                                                           fireTreatmentStarted(doctor, patient);
                                                           return true;
                                            }
                              }
                              return false;
               }
}



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.

Expand
titleLF

 

I FXML-koden ser en at det finnes en Label med fx:id="patientMessage". Her må man skjønne/gjette at det er dette objektet som skal oppdateres når en pasient skal gå til en doktor. En kan også lese at controlleren er en (instans av) TreatmentUnitController.

TreatmentUnitController:

-          Label patientMessage i FXML må knyttes til en variabel i TreatmentUnitController, gjennom å bruke @FXML-annotasjonen og Label-type.

-          Konstruktøren oppretter en ny TreatmentUnit. Så er det litt valgfritt hva som skjer, men den viktige biten er at man i konstruktøren også må opprette forbindelsen mellom TreatmentUnitController som lytter/observatør og TreatmentUnit som observert vha. et kall til addTreatmentListener-metoden fra 5a.

-          TreatmentUnitController må implementere lyttergrensesnittet og (den ene) lyttermetoden må oppdaterer status på Label-objektet.

 

Code Block
languagejava
public class TreatmentUnitController implements TreatmentListener
{

 

               private TreatmentUnit treatmentUnit;

 

               public TreatmentUnitController() {

                              treatmentUnit = new TreatmentUnit();

                             

              // …

 

                             
 {
 
               private TreatmentUnit treatmentUnit;
 
               public TreatmentUnitController() {
                              treatmentUnit = new TreatmentUnit();
                              
              // ...
 
                              treatmentUnit.addTreatmentListener(this);

               }

 

               @FXML

               private Label patientMessage;

 

               @Override

               public void treatmentStarted(Doctor doctor, Patient patient, TreatmentUnit treatmentUnit) {

                             

               }
 
               @FXML
               private Label patientMessage;
 
               @Override
               public void treatmentStarted(Doctor doctor, Patient patient, TreatmentUnit treatmentUnit) {
                              patientMessage.setText(patient.getName() + " bes  til doktor " + doctor.getName());

               }


               }
}