Event Bus et le pattern Observer en Kotlin : architecture des applications orientées événements
Le développement moderne en Kotlin, en particulier dans le contexte d'Android et des applications serveur, nécessite des mécanismes flexibles d'interaction entre les composants. Le pattern Observer et son implémentation pratique — l'Event Bus — offrent une solution élégante pour une architecture faiblement couplée, où un objet (l'éditeur) notifie plusieurs abonnés de la survenue d'un événement, sans connaître leur implémentation spécifique. Dans cet article, nous allons voir comment construire votre propre Event Bus en Kotlin, en utilisant les coroutines et un typage sécurisé.
Pattern Observer : base théorique
Le pattern Observer (Observateur) définit une relation "un à plusieurs" entre les objets. Lorsque l'état du sujet (éditeur) change, tous les objets dépendants (observateurs) sont automatiquement notifiés et mis à jour. En Kotlin, ce pattern peut être implémenté via des interfaces, des lambdas ou des constructions plus avancées.
Éléments clés du pattern :
- Sujet (Subject) — l'objet qui gère la liste des observateurs et envoie les notifications.
- Observateur (Observer) — l'objet qui reçoit les notifications du sujet.
- SujetConcret (ConcreteSubject) — implémentation concrète du sujet avec son propre état.
- ObservateurConcret (ConcreteObserver) — implémentation concrète de l'observateur, réagissant aux changements.
Exemple d'implémentation simple en Kotlin :
interface Observer { fun update(event: String)}
class ConcreteSubject { private val observers = mutableListOf<Observer>()
fun attach(observer: Observer) { observers.add(observer) }
fun detach(observer: Observer) { observers.remove(observer) }
fun notifyObservers(event: String) { observers.forEach { it.update(event) } }}
class ConcreteObserver(private val name: String) : Observer { override fun update(event: String) { println("$name a reçu l'événement : $event") }}
fun main() { val subject = ConcreteSubject() val observer1 = ConcreteObserver("Observateur 1") val observer2 = ConcreteObserver("Observateur 2")
subject.attach(observer1) subject.attach(observer2) subject.notifyObservers("Données mises à jour")}Event Bus : un bus d'événements pratique
L'Event Bus est un mécanisme centralisé qui étend le pattern Observer. Au lieu que chaque sujet gère ses propres observateurs, on crée un bus unique par lequel transitent tous les événements. Cela simplifie la communication entre les composants, en particulier dans les applications complexes.
Avantages de l'Event Bus :
- Faible couplage : les éditeurs et les abonnés ne se connaissent pas mutuellement.
- Simplicité d'ajout de nouveaux abonnés sans modifier le code existant.
- Possibilité de filtrer les événements par type ou par tag.
- Prise en charge du traitement asynchrone via les coroutines.
Implémentation d'un Event Bus en Kotlin avec les coroutines
Une implémentation moderne d'Event Bus doit tenir compte du multithreading et éviter les fuites de mémoire. L'utilisation de Coroutines et de Channel permet de créer un bus thread-safe et efficace.
Implémentation de base d'un Event Bus :
import kotlinx.coroutines.*import kotlinx.coroutines.channels.Channelimport kotlinx.coroutines.flow.*
sealed class Event { data class UserLoggedIn(val userId: String) : Event() data class DataUpdated(val data: String) : Event() object NetworkError : Event()}
class EventBus { private val _events = MutableSharedFlow<Event>(extraBufferCapacity = 64) val events: SharedFlow<Event> = _events.asSharedFlow()
suspend fun publish(event: Event) { _events.emit(event) }
fun subscribe(scope: CoroutineScope, action: (Even