diff --git a/src/Ex2.scala b/src/Ex2.scala index 327e80d0625def02b1ec5880d5cb410a9801527c..72ccb3fe117191d8324d2b4e323bdd61e31abbc9 100644 --- a/src/Ex2.scala +++ b/src/Ex2.scala @@ -1,3 +1,4 @@ +package ex2 // Create a class model of the hospital including the following: // units // doctors diff --git a/src/Ex4.scala b/src/Ex4.scala new file mode 100644 index 0000000000000000000000000000000000000000..272f20b643d4947bb2560b149300924a9da30566 --- /dev/null +++ b/src/Ex4.scala @@ -0,0 +1,116 @@ +package ex4 +// Create a class model of the hospital including the following: +// units +// doctors +// patients +// Create methods relative to these classes e.g. +// create consultation +// reserve appointment +// Include one use of Type Restriction, why is it useful? +// Include one example of Covariance and one of Contravariance, why are they useful? + +enum Expertise { + case Cardiology + case Surgery + case Endocrinology + case Neurology + case Ophthalmology + case Pediatrics + case Psychiatry + case Urology +} + +extension (s: String) + def isName: Boolean = s.matches("[A-Z][a-z]+") + +trait Person { + val name: String + var age: Int +} + +class Doctor(val name: String, var age: Int, val expertises: List[Expertise]) + extends Person { + def consult(patient: Patient)(using hospital: Hospital) = + println(s"Doctor $name is consulting patient ${patient.name}" + + s"with condition ${patient.condition}.") + given Conversion[Doctor, Patient] = (d: Doctor) => Patient("mental", d.name, d.age) +} + +case class Patient(condition: String, val name: String, var age: Int) + extends Person { + def requestAppointment(using hospital: Hospital) = { + val unit = + hospital.units.find(u => + u.areaOfExpertise == hospital.processCondition(this.condition) + ) + if unit.isDefined then unit.get.addPatient(this) + else throw Exception("No unit available for this condition.") + } +} + +// Covariance is useful here because a list of doctors is a list of persons +// and we might want to use a list of doctors in a method that expects a list of persons +// Type restriction is useful here because we want to restrict the type of the list to only Doctors +case class UnitPersonnel[+T <: Doctor](list: List[T]) + +class HospitalUnit( + var areaOfExpertise: Expertise, + var doctors: UnitPersonnel[Doctor], + var patients: List[Patient] +) { + def assignDoctor(doctor: Doctor): Unit = doctors = UnitPersonnel( + doctors.list :+ doctor + ) + + def addPatient(patient: Patient)(using hospital: Hospital) = { + patients = patients :+ patient + doctors.list.last.consult(patient) + } +} + +// Contravariance is useful here because we might create summaries of generic types +abstract class SummaryPrinter[-T]: + def printSummary(value: T): String + +class Hospital { + var units: List[HospitalUnit] = List() + var doctors: List[Doctor] = List() + def processCondition(condition: String) = condition match { + case "heart" => Expertise.Cardiology + case "brain" => Expertise.Neurology + case "eye" => Expertise.Ophthalmology + case "kidney" => Expertise.Urology + case "bone" => Expertise.Surgery + case "endocrine" => Expertise.Endocrinology + case "child" => Expertise.Pediatrics + case "mental" => Expertise.Psychiatry + case _ => Expertise.Surgery + } + def addDoctor(d: Doctor): Unit = { + if !d.name.isName then throw Exception("Invalid name.") + for expertise <- d.expertises do { + val unit = units.find(_.areaOfExpertise == expertise) + if unit.isDefined then { + doctors = doctors :+ d + unit.get.assignDoctor(d) + return + } + } + addUnit(d.expertises.head, d) + } + def addUnit(expertise: Expertise, doctor: Doctor) = { + val unit = + new HospitalUnit( + expertise, + UnitPersonnel[Doctor](List(doctor)), + List() + ) + units = units :+ unit + } + def listDoctors = doctors.foreach(PersonSummaryPrinter.printSummary(_)) + + object PersonSummaryPrinter extends SummaryPrinter[Person](): + def printSummary(person: Person): String = + s"${person.name} is ${person.age} years old." + +} diff --git a/test-src/Ex4Spec.scala b/test-src/Ex4Spec.scala new file mode 100644 index 0000000000000000000000000000000000000000..ba2ecf03a6892398942ba09ea52cbbdc6981ffd4 --- /dev/null +++ b/test-src/Ex4Spec.scala @@ -0,0 +1,33 @@ +package ex4 + +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpec + +class Ex4Spec extends AnyWordSpec with Matchers { + "An hospital" should { + val hospital = new Hospital + given Hospital = hospital + "have a list of units" in { + hospital.units should be(List()) + } + "have a list of doctors" in { + hospital.doctors should be(List()) + } + "accept new doctors" in { + val doctor = new Doctor("John", 30, List(Expertise.Cardiology)) + hospital.addDoctor(doctor) + hospital.units should have length 1 + } + "accept appointments" in { + val patient = Patient("heart", "John", 30) + patient.requestAppointment + hospital.units.head.patients should have length 1 + } + "throw an exception if doctor name is invalid" in { + val doctor = new Doctor("23864", 30, List(Expertise.Cardiology)) + an[Exception] should be thrownBy { + hospital.addDoctor(doctor) + } + } + } +}