Le but de cet exercice est d'implémenter un petit système de notifications simple, dans lequel un `Notifier` peut prévenir d'autres objets, les `Receiver`, qu'un événement intéressant s'est produit. Les seuls *notifiers* que nous créerons sont des générateurs d'objets qui notifient leurs *receivers* dès qu'un nouvel objet a été créé. Ces différents éléments sont architecturés comme décrit dans le diagramme suivant : ```plantuml @startuml skinparam classAttributeIconSize 0 skinparam monochrome true skinparam shadowing false skinparam linetype ortho skinparam class { BackgroundColor transparent } skinparam note { BackgroundColor transparent } hide circle abstract class Notifier { +notifyAllReceivers(data : Any) +addReceiver(rec : Receiver) {exceptions = FullException} +removeLastReceiver(rec : Receiver) } interface Receiver { +receive(data : Any) } abstract class Generator { -millisecToWait : Int -continueGeneration : Boolean -thread : Thread +Generator(secondsToWait : Float) +startGenerate() +stopGenerate() +{abstract}generate() : Any } class FullException extends Exception { } class FloatGenerator { +FloatGenerator() +FloatGenerator(secondsToWait : Float, upperBound : Float) +generate() : Float } class Person { -firstName : String -lastName : String +Person(firstName : String, lastName : String) +getFirstName() : String +getLastName() : String } class PersonGenerator { -{static}lastNames : String[] = {"Dupont", "Schmidt", "Clavore"} -{static}firstNames : String[] = {"Jean", "Paul", "Marie"} +PersonGenerator(secondsToWait : Float) +generate() : Person } class ConsolePrinter { -id : String +ConsolePrinter(id : String) +receive(data : Any) } class NumberAccumulator { -accumulatedValue : Double +receive(data : Any) +getAccumulatedValue() : Double } Notifier ---> "0..3\n-receivers" Receiver Notifier <|-- Generator Generator <|-- FloatGenerator Generator <|-- PersonGenerator PersonGenerator ..> Person Receiver <|.. ConsolePrinter Receiver <|.. NumberAccumulator @enduml ``` Il vous est demandé de coder cette conception en Kotlin. **Attention**, le but n'est pas d'aller le plus rapidement possible, mais de prendre le temps d'explorer les possibilités du langage pour essayer d'être concis et d'adopter le Kotlin spirit. Les noms des méthodes sont explicites, vous devez être capable de déterminer quoi code. Voici juste quelques informations supplémentaires qui pourront vous aider : * Les *receivers* seront stockés dans un simple tableau; * La méthode `addReceiver(…)` lèvera une exception lorsque le tableau des *receivers* sera plein; * La classe `ConsolePrinter`, dès qu'elle sera notifiée de la réception d'une donnée, affichera cette donnée sur la sortie standard, préfixée par «id:» où id est évidemment l'`id` du `ConsolePrinter`; * La classe `NumberAccumulator`, dès qu'elle sera notifiée de la réception d'une donnée, ajoutera cette donnée à son `accumulatedValue` si cette dernière est un nombre (peu importe son type réel); * La classe `Generator` doit permet de générer un objet (grâce à `generate(…)`) toutes les `secondsToWait` secondes. Chaque générateur possèdera son thread pour effectuer cette génération. * La classe `FloatGenerator` générera un flottant compris entre 0 et `upperBound`. Sans informations explicites, cette génération se fera toutes les secondes et la borne supérieure sera 1000. * La classe `PersonGenerator` génèrera des personnes dont la combinaison «Prénom Nom» est choisie aléatoirement parmi les tableaux `firstNames` et `lastNames`. Le programme suivant devra compiler : ```kotlin fun main() { val personGen = PersonGenerator(0.8f) val numGen = FloatGenerator(0.2f, 91827364.5f) val acc = NumberAccumulator() personGen.addReceiver(::println) personGen.addReceiver(acc) numGen.addReceiver(::println) numGen.addReceiver(acc) personGen.startGenerate() numGen.startGenerate() Thread.sleep(5000) personGen.stopGenerate() numGen.stopGenerate() println(acc.accumulatedValue) } ```