确实,每次创建事件都传 target 很繁琐。让我提供几个更优雅的方案:
方案1: 事件工厂模式 (推荐)
让 Colleen 实例作为事件发射器:
class Colleen {
val events = EventBus()
// 扩展函数风格的事件发射
fun emit(eventFactory: (Colleen) -> AppEvent) {
val event = eventFactory(this)
events.emit(event)
}
// 或者更简洁的 DSL 风格
inline fun <reified T : AppEvent> emit(builder: (Colleen) -> T) {
events.emit(builder(this))
}
}
// 事件定义简化
sealed class AppEvent(
open val source: Colleen, // 改名为 source 更语义化
val bubbles: Boolean = true,
) { /* ... */ }
class ServerStarted(override val source: Colleen) : AppEvent(source, false)
// 使用时非常简洁
app.emit { ServerStarted(it) }
app.emit { RequestReceived(it, request) }
// 或者提供便捷方法
fun Colleen.emitServerStarted() {
emit { ServerStarted(it) }
}方案2: 上下文接收者 (Kotlin 风格)
// 事件构建器使用 context receivers (Kotlin 1.6.20+)
context(Colleen)
fun serverStarted() = ServerStarted(this@Colleen)
context(Colleen)
fun requestReceived(request: Request) = RequestReceived(this@Colleen, request)
// 在 Colleen 内部使用
class Colleen {
fun start() {
with(this) {
events.emit(serverStarted())
}
}
}方案3: 事件不持有 target (最激进)
核心思想: target 本质上是发射事件的主体,为什么要存在事件对象里?
sealed class AppEvent(
val bubbles: Boolean = true,
) {
// target 不再是事件的属性
var currentTarget: Colleen? = null
internal set
@Volatile
private var _propagationStopped: Boolean = false
val isPropagationStopped: Boolean get() = _propagationStopped
fun stopPropagation() {
_propagationStopped = true
}
val timestamp: Long = System.currentTimeMillis()
}
// 事件定义超级简洁
class ServerStarted : AppEvent(bubbles = false)
class ServerStarting : AppEvent(bubbles = false)
class RequestReceived(
val request: Request,
var transform: ((Request) -> Request)? = null
) : AppEvent(bubbles = false)
class MiddlewareExecuting(
val ctx: Context,
val node: MiddlewareNode
) : AppEvent()
// EventBus 在发射时绑定 target
class EventBus(
private val owner: Colleen // EventBus 知道自己的所有者
) {
internal var parent: EventBus? = null
fun <T : AppEvent> emit(event: T) {
// 在发射时设置 target
event.currentTarget = owner
val eventListeners = listeners[event::class.java]
eventListeners?.forEach { listener ->
if (event.isPropagationStopped) return@forEach
@Suppress("UNCHECKED_CAST")
try {
(listener as EventListener<T>).handler.accept(event)
} catch (e: Exception) {
logger.error("Error in event listener for ${event::class.simpleName}", e)
}
}
if (event.bubbles && !event.isPropagationStopped && parent != null) {
val originalTarget = event.currentTarget
event.currentTarget = parent.owner // 更新为父节点
parent.emit(event)
event.currentTarget = originalTarget
}
}
}
// Colleen 使用
class Colleen {
val events = EventBus(this)
fun start() {
events.emit(ServerStarting()) // 超级简洁!
// ...
events.emit(ServerStarted())
}
fun handleRequest(request: Request) {
events.emit(RequestReceived(request))
}
}
// 监听器可以通过 currentTarget 获取发射者
app.events.on<ServerStarted> { event ->
println("Server started on ${event.currentTarget}")
}方案4: 混合方案 (灵活性最高)
sealed class AppEvent(
val bubbles: Boolean = true,
) {
// target 改为可空,默认为 null
var source: Colleen? = null
internal set
var currentTarget: Colleen? = null
internal set
// 其他属性...
}
class EventBus(
private val owner: Colleen
) {
fun <T : AppEvent> emit(event: T) {
// 如果事件没有 source,自动设置为当前 owner
if (event.source == null) {
event.source = owner
}
event.currentTarget = owner
// ... 发射逻辑
}
}
// 使用时两种风格都支持
events.emit(ServerStarted()) // 自动使用当前 owner
// 特殊场景也可以显式指定
val customEvent = RequestReceived(request).apply { source = otherApp }
events.emit(customEvent)🎯 我的推荐: 方案3 (事件不持有 target)
理由:
语义更清晰: 事件是"发生的事情",target 是"谁发出的",它们是两个独立的概念
使用最简洁:
emit(ServerStarted())vsemit(ServerStarted(this))避免循环引用: 事件不再持有 Colleen 引用,GC 友好
更符合事件本质: 参考 DOM Events,target 是运行时绑定的,不是事件的固有属性
完整实现示例
sealed class AppEvent(
val bubbles: Boolean = true,
) {
// source: 最初触发事件的对象 (不可变)
var source: Colleen? = null
internal set
// currentTarget: 当前处理事件的对象 (冒泡过程中会变化)
var currentTarget: Colleen? = null
internal set
@Volatile
private var _propagationStopped: Boolean = false
val isPropagationStopped: Boolean get() = _propagationStopped
fun stopPropagation() {
_propagationStopped = true
}
val timestamp: Long = System.currentTimeMillis()
}
// ============ 生命周期事件 ============
class ServerStarting : AppEvent(bubbles = false)
class ServerStarted : AppEvent(bubbles = false)
class ServerStopping : AppEvent(bubbles = false)
class ServerStopped : AppEvent(bubbles = false)
// ============ 配置/结构事件 ============
class MiddlewareAdded(val node: MiddlewareNode) : AppEvent()
class RouteAdded(val method: String, val path: String, val handler: Any) : AppEvent()
class SubAppMounted(val node: MountNode) : AppEvent()
// ============ 请求处理事件 ============
class RequestReceived(
val request: Request,
var transform: ((Request) -> Request)? = null
) : AppEvent(bubbles = false)
class MiddlewareExecuting(
val ctx: Context,
val node: MiddlewareNode
) : AppEvent()
class MiddlewareExecuted(
val ctx: Context,
val node: MiddlewareNode,
val duration: Long
) : AppEvent()
class HandlerExecuting(
val ctx: Context,
val node: RouteNode,
) : AppEvent()
class HandlerExecuted(
val ctx: Context,
val node: RouteNode,
val duration: Long
) : AppEvent()
// ============ 响应事件 ============
class ResponseGenerated(
val ctx: Context,
var transform: ((ResponseBody) -> ResponseBody)? = null
) : AppEvent(bubbles = false)
class ResponseSent(
val bytesWritten: Long,
val duration: Long
) : AppEvent(bubbles = false)
// ============ 错误事件 ============
class ExceptionThrown(
val exception: Exception,
val ctx: Context,
) : AppEvent()
class ErrorHandled(
val exception: Exception,
val ctx: Context,
) : AppEvent()
// ============ EventBus ============
class EventBus(
private val owner: Colleen
) {
internal var parent: EventBus? = null
@PublishedApi
internal val listeners =
ConcurrentHashMap<Class<out AppEvent>, CopyOnWriteArrayList<EventListener<out AppEvent>>>()
inline fun <reified T : AppEvent> on(noinline handler: (T) -> Unit): EventListener<T> {
return on(T::class.java, Consumer(handler))
}
fun <T : AppEvent> on(clazz: Class<T>, handler: Consumer<T>): EventListener<T> {
val listener = EventListener(clazz, handler)
listeners.computeIfAbsent(clazz) { CopyOnWriteArrayList() }.add(listener)
return listener
}
fun off(listener: EventListener<*>) {
listeners[listener.eventType]?.remove(listener)
}
fun <T : AppEvent> emit(event: T) {
// 首次发射时设置 source
if (event.source == null) {
event.source = owner
}
// 设置当前处理者
event.currentTarget = owner
val eventListeners = listeners[event::class.java]
eventListeners?.forEach { listener ->
if (event.isPropagationStopped) return@forEach
@Suppress("UNCHECKED_CAST")
try {
(listener as EventListener<T>).handler.accept(event)
} catch (e: Exception) {
logger.error("Error in event listener for ${event::class.simpleName}", e)
}
}
// 冒泡
if (event.bubbles && !event.isPropagationStopped && parent != null) {
parent.emit(event)
}
}
}
// ============ 使用示例 ============
class Colleen {
val events = EventBus(this)
fun start() {
events.emit(ServerStarting()) // ✨ 超级简洁
// ...
events.emit(ServerStarted())
}
}
// 监听
app.events.on<ServerStarted> { event ->
println("Started by: ${event.source}") // 最初触发者
println("Current handler: ${event.currentTarget}") // 当前处理者
}这样设计后,创建事件变得非常自然,同时保留了完整的事件源追踪能力。你觉得这个方案如何?