确实,每次创建事件都传 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)

理由:

  1. 语义更清晰: 事件是"发生的事情",target 是"谁发出的",它们是两个独立的概念

  2. 使用最简洁: emit(ServerStarted()) vs emit(ServerStarted(this))

  3. 避免循环引用: 事件不再持有 Colleen 引用,GC 友好

  4. 更符合事件本质: 参考 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}")  // 当前处理者
}

这样设计后,创建事件变得非常自然,同时保留了完整的事件源追踪能力。你觉得这个方案如何?