观察者模式:优雅实现对象间的动态通知机制(行为模式)

观察者模式:实现动态通知

在软件系统中,对象之间经常存在一种依赖关系:当一个对象的状态发生改变时,所有依赖于它的对象都需要得到通知并自动更新。观察者模式正是管理这种依赖关系的完美解决方案。

🎯 模式意图

观察者模式是一种行为设计模式,它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。

核心思想:将观察者注册到被观察对象上,被观察对象状态变化时自动通知所有观察者,实现松耦合的发布-订阅机制。

🔍 现实世界比喻

想象一下报纸订阅系统:

  • 报社(被观察者)出版新报纸

  • 订阅者(观察者)向报社订阅

  • 每当有新报纸出版时,报社自动将报纸发送给所有订阅者

  • 订阅者可以随时订阅或取消订阅

  • 报社不需要知道订阅者的具体信息,只需要维护订阅列表

这种"发布-订阅"的机制正是观察者模式的精髓!

💻 传统处理方式的问题

先来看看我们想要避免的"反模式":

// ❌ 紧耦合的实现:被观察者需要知道所有观察者的细节
class WeatherStation {
    private val phoneDisplay = PhoneDisplay()
    private val tvDisplay = TVDisplay()
    private val webDisplay = WebDisplay()
    
    fun setTemperature(temp: Float) {
        // 需要手动调用每个显示设备
        phoneDisplay.updateTemperature(temp)
        tvDisplay.updateTemperature(temp)
        webDisplay.updateTemperature(temp)
    }
    
    // 添加新显示器需要修改WeatherStation类
    fun addNewDisplay(newDisplay: SomeNewDisplay) {
        // 需要修改内部结构,违反开闭原则
    }
}

问题分析

  • 紧耦合:被观察者需要知道所有观察者的具体类型

  • 违反开闭原则:新增观察者需要修改被观察者代码

  • 难以维护:观察者越多,被观察者越复杂

  • 无法动态管理:无法在运行时动态添加/删除观察者

🚀 观察者模式的优雅实现

基础观察者模式

// 观察者接口
interface Observer {
    fun update(temperature: Float, humidity: Float, pressure: Float)
}

// 被观察者接口
interface Subject {
    fun registerObserver(observer: Observer)
    fun removeObserver(observer: Observer)
    fun notifyObservers()
}

// 具体被观察者:气象站
class WeatherStation : Subject {
    private val observers = mutableListOf<Observer>()
    private var temperature: Float = 0.0f
    private var humidity: Float = 0.0f
    private var pressure: Float = 0.0f
    
    override fun registerObserver(observer: Observer) {
        observers.add(observer)
    }
    
    override fun removeObserver(observer: Observer) {
        observers.remove(observer)
    }
    
    override fun notifyObservers() {
        observers.forEach { it.update(temperature, humidity, pressure) }
    }
    
    // 气象数据变化时自动通知所有观察者
    fun setMeasurements(temperature: Float, humidity: Float, pressure: Float) {
        this.temperature = temperature
        this.humidity = humidity
        this.pressure = pressure
        measurementsChanged()
    }
    
    private fun measurementsChanged() {
        notifyObservers()
    }
}

具体观察者实现

// 手机显示设备
class PhoneDisplay : Observer {
    private var currentTemp: Float = 0.0f
    
    override fun update(temperature: Float, humidity: Float, pressure: Float) {
        this.currentTemp = temperature
        display()
    }
    
    private fun display() {
        println("📱 手机显示:当前温度 ${currentTemp}°C")
    }
}

// 电视显示设备
class TVDisplay : Observer {
    private var currentTemp: Float = 0.0f
    private var currentHumidity: Float = 0.0f
    
    override fun update(temperature: Float, humidity: Float, pressure: Float) {
        this.currentTemp = temperature
        this.currentHumidity = humidity
        display()
    }
    
    private fun display() {
        println("📺 电视显示:温度 ${currentTemp}°C,湿度 ${currentHumidity}%")
    }
}

// Web显示设备
class WebDisplay : Observer {
    private var currentPressure: Float = 0.0f
    
    override fun update(temperature: Float, humidity: Float, pressure: Float) {
        this.currentPressure = pressure
        display()
    }
    
    private fun display() {
        println("🌐 网页显示:当前气压 ${currentPressure}hPa")
    }
}

客户端使用

fun main() {
    // 创建被观察者
    val weatherStation = WeatherStation()
    
    // 创建观察者
    val phoneDisplay = PhoneDisplay()
    val tvDisplay = TVDisplay()
    val webDisplay = WebDisplay()
    
    // 注册观察者
    weatherStation.registerObserver(phoneDisplay)
    weatherStation.registerObserver(tvDisplay)
    weatherStation.registerObserver(webDisplay)
    
    println("=== 第一次数据更新 ===")
    weatherStation.setMeasurements(25.0f, 65.0f, 1013.25f)
    
    println("\n=== 移除电视显示后更新 ===")
    weatherStation.removeObserver(tvDisplay)
    weatherStation.setMeasurements(28.0f, 60.0f, 1012.50f)
    
    println("\n=== 添加新的观察者后更新 ===")
    val smartWatchDisplay = object : Observer {
        override fun update(temperature: Float, humidity: Float, pressure: Float) {
            println("⌚ 智能手表:${temperature}°C")
        }
    }
    weatherStation.registerObserver(smartWatchDisplay)
    weatherStation.setMeasurements(22.0f, 70.0f, 1014.00f)
}

🌟 高级特性:推模式 vs 拉模式

推模式(Push Model)

// 推模式:被观察者将数据推送给观察者
interface PushObserver {
    fun update(data: WeatherData)
}

data class WeatherData(
    val temperature: Float,
    val humidity: Float,
    val pressure: Float,
    val windSpeed: Float
)

class PushWeatherStation : Subject {
    // ... 注册、移除逻辑类似
    
    fun notifyObservers(data: WeatherData) {
        observers.forEach { it.update(data) }
    }
}

拉模式(Pull Model)

// 拉模式:观察者从被观察者拉取需要的数据
interface PullSubject {
    fun getTemperature(): Float
    fun getHumidity(): Float
    fun getPressure(): Float
    // ... 其他getter方法
}

class PullObserver {
    fun update(subject: PullSubject) {
        // 观察者只拉取自己需要的数据
        val temp = subject.getTemperature()
        val humidity = subject.getHumidity()
        // ... 处理数据
    }
}

🏗️ 事件总线的全局观察者

对于需要全局通信的场景,可以实现事件总线:

// 全局事件总线(单例)
object EventBus {
    private val subscribers = mutableMapOf<Class<*>, MutableList<(Any) -> Unit>>()
    
    fun <T : Any> subscribe(eventType: Class<T>, subscriber: (T) -> Unit) {
        subscribers.getOrPut(eventType) { mutableListOf() }.add { 
            subscriber(it as T) 
        }
    }
    
    fun <T : Any> publish(event: T) {
        subscribers[event.javaClass]?.forEach { it(event) }
    }
}

// 定义事件类
data class UserLoginEvent(val userId: String, val loginTime: Long)
data class OrderCreatedEvent(val orderId: String, val amount: Double)

// 使用事件总线
class LoginService {
    fun login(userId: String) {
        // ... 登录逻辑
        EventBus.publish(UserLoginEvent(userId, System.currentTimeMillis()))
    }
}

class AnalyticsService {
    init {
        EventBus.subscribe(UserLoginEvent::class.java) { event ->
            println("📊 记录用户登录统计: ${event.userId}")
        }
    }
}

📊 模式结构解析

Subject(被观察者) → Observer(观察者接口)
     ↑                      ↑
ConcreteSubject        ConcreteObserver
     |                      |
维护观察者列表           实现update方法
notifyObservers()       响应状态变化
  • Subject:被观察者接口,定义注册、移除、通知方法

  • ConcreteSubject:具体被观察者,维护观察者列表,状态改变时通知观察者

  • Observer:观察者接口,定义更新方法

  • ConcreteObserver:具体观察者,实现更新逻辑

🎯 优势总结

优势

说明

松耦合

被观察者和观察者之间抽象耦合,彼此不知道对方细节

动态关系

可以在运行时动态添加、删除观察者

开闭原则

新增观察者无需修改被观察者代码

广播通信

支持一对多的通信方式,一个变化通知多个对象

🔄 与其他模式的关系

  • 与中介者模式:都管理对象间通信,但观察者通过松耦合,中介者通过集中控制

  • 与责任链模式:观察者广播给所有接收者,责任链顺序传递直到被处理

  • 与命令模式:命令对象可以作为通知消息在观察者间传递

💡 实际应用场景

Android LiveData

class MyViewModel : ViewModel() {
    private val _userData = MutableLiveData<String>()
    val userData: LiveData<String> = _userData
    
    fun updateUserData(newData: String) {
        _userData.value = newData
    }
}

// Activity中观察数据变化
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        val viewModel = ViewModelProvider(this)[MyViewModel::class.java]
        viewModel.userData.observe(this) { data ->
            // 数据变化时自动更新UI
            updateUI(data)
        }
    }
}

React/Vue 响应式系统

// Vue 3 的响应式原理类似观察者模式
const state = reactive({ count: 0 })

// 观察状态变化
watchEffect(() => {
    console.log(`计数变为: ${state.count}`)
})

// 触发通知
state.count++ // 自动打印"计数变为: 1"

Java Swing 事件监听

button.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {
        // 按钮点击时被通知
        System.out.println("按钮被点击了!");
    }
});

Kotlin 属性监听

// 使用委托属性实现属性监听
class ObservableProperty<T>(
    initialValue: T,
    private val onChange: (oldValue: T, newValue: T) -> Unit
) {
    var value: T = initialValue
        set(newValue) {
            val oldValue = field
            field = newValue
            onChange(oldValue, newValue)
        }
}

class User {
    var name: String by ObservableProperty("") { old, new ->
        println("名字从 '$old' 改为 '$new'")
    }
}

🚀 最佳实践建议

1. 防止内存泄漏

// 在Android中使用Lifecycle避免内存泄漏
class MyFragment : Fragment() {
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        
        viewModel.data.observe(viewLifecycleOwner) { data ->
            // 自动在合适的生命周期取消观察
            updateUI(data)
        }
    }
}

2. 异步通知处理

// 支持异步通知的观察者
interface AsyncObserver {
    suspend fun updateAsync(data: WeatherData)
}

class AsyncWeatherStation {
    private val observers = mutableListOf<AsyncObserver>()
    
    suspend fun notifyObserversAsync(data: WeatherData) {
        observers.forEach { observer ->
            // 异步通知每个观察者
            launch { observer.updateAsync(data) }
        }
    }
}

3. 通知过滤机制

// 只通知对特定事件感兴趣的观察者
class FilteredEventBus {
    fun <T : Any> subscribe(
        eventType: Class<T>,
        filter: (T) -> Boolean,
        subscriber: (T) -> Unit
    ) {
        // 只有满足过滤条件的事件才会被通知
    }
}

🌟 响应式编程扩展

观察者模式是现代响应式编程的基石:

// 简单的响应式流实现
class ReactiveStream<T> {
    private val subscribers = mutableListOf<(T) -> Unit>()
    
    fun subscribe(onNext: (T) -> Unit) {
        subscribers.add(onNext)
    }
    
    fun emit(value: T) {
        subscribers.forEach { it(value) }
    }
}

// 使用响应式流
val clickStream = ReactiveStream<Unit>()
val dataStream = ReactiveStream<String>()

// 组合流
clickStream.subscribe {
    dataStream.emit("按钮点击获取的数据")
}

dataStream.subscribe { data ->
    println("收到数据: $data")
}

总结

观察者模式通过发布-订阅机制完美解决了对象间的动态依赖关系。它让软件系统更加:

解耦:观察者和被观察者之间松耦合

灵活:支持运行时动态管理观察关系

可扩展:符合开闭原则,易于扩展新观察者

响应式:为响应式编程奠定基础

从GUI事件处理到响应式框架,从状态管理到消息系统,观察者模式无处不在。掌握这一模式,你将能设计出更加灵活、可维护的软件架构!

内容概要:本文围绕EKF SLAM(扩展卡尔曼滤波同步定位与地图构建)的性能展开多项对比实验研究,重点分析在稀疏与稠密landmark环境下、预测与更新步骤同时进行与非同时进行的情况下的系统性能差异,并进一步探讨EKF SLAM在有色噪声干扰下的鲁棒性表现。实验考虑了不确定性因素的影响,旨在评估不同条件下算法的定位精度与地图构建质量,为实际应用中EKF SLAM的优化提供依据。文档还提及多智能体系统在遭受DoS攻击下的弹性控制研究,但核心内容聚焦于SLAM算法的性能测试与分析。; 适合人群:具备一定机器人学、状态估计或自动驾驶基础知识的科研人员及工程技术人员,尤其是从事SLAM算法研究或应用开发的硕士、博士研究生和相关领域研发人员。; 使用场景及目标:①用于比较EKF SLAM在不同landmark密度下的性能表现;②分析预测与更新机制同步与否对滤波器稳定性与精度的影响;③评估系统在有色噪声等非理想观测条件下的适应能力,提升实际部署中的可靠性。; 阅读建议:建议结合MATLAB仿真代码进行实验复现,重点关注状态协方差传播、观测更新频率与噪声模型设置等关键环节,深入理解EKF SLAM在复杂环境下的行为特性。稀疏 landmark 与稠密 landmark 下 EKF SLAM 性能对比实验,预测更新同时进行与非同时进行对比 EKF SLAM 性能对比实验,EKF SLAM 在有色噪声下性能实验
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值