ts实现简易观察者模式

观察者模式是一种设计模式,用于当对象状态改变时通知其他依赖它的对象。文中通过气象站的例子,展示了如何创建一个主题接口和观察者接口,以及如何实现这些接口来跟踪和更新状态。此外,还提到了Vue的响应式原理也基于观察者模式。

什么是观察者模式

观察者模式能让你时刻知悉对象状态的变化的一种设计模式,是一种一对多依赖的关系,比如报纸的订阅

生活中随处可见的观察者模式(猎头与求职者):

headfirst设计模式气象站案例

通知更新的方式有两种: 主题推给观察者和观察者自己去主题拉取两种方式,大部分实现都采用推,这里也采用推的方式实现

气象站需要设计一个主题(Subject),它包含三个状态:气温,湿度和气压,要求主题的状态发生改变后要通知三个展示状态的布告板进行更新,要求布告板的类型是可扩展的,主题的状态是可扩展的

这里把气象站看做是一个对象,它有一些状态并且是可扩展的,这里用一个对象表示,扩展状态往里面加就行了:

type WeatherData = Partial<{
  temperature: string // 气温
  humidity: string // 湿度
  pressure: string // 气压
}>

把每个布告板看做是一个观察者,它由update(更新)和display(展示)两个功能组成,update方法是必备的且统一叫update,由气象站主题状态改变时调用update更新,这个过程就是通知更新

因为不同类型的布告板展示内容一样,所以观察者应该是一个接口

interface Observer {
  update: (data: WeatherData) => void // 更新状态
  display: (data: WeatherData) => void // 展示主题
}

主题也应该是一个接口,它包含了一些公共api:

interface Subject {
  changed: boolean // 标记状态是否更新
  observers: Observer[] // 所有观察者的集合
  setMeasurementsChanged?: (data: WeatherData) => void // 设置检测值
  measurementsChanged?: (data: WeatherData) => void // 检测值发生变化时调用通知观察者更新
  addObserver: (observer: Observer) => number // 注册观察者
  deleteObserver: (observer: Observer) => boolean // 取消订阅观察者
  notifyObservers: (data: WeatherData) => void // 通知观察者更新  推送最新状态
  setChanged: () => void // 状态变更的标记位
}

定义气象站类实现主题接口:

class Weather implements Subject {
  state: WeatherData // 记录气象站状态
  changed: boolean // 是否更新的标记位
  observers: Observer[] // 观察者集合
  constructor() {
    this.observers = [] // 初始化观察者集合
  }
  // 状态变化的钩子
  measurementsChanged(data: WeatherData) {
    console.log('气象站主题更新了,通知观察者更新--------------------')
    this.notifyObservers(data) //
  }
  // 这个方法状态有新值时供外界调用触发更新
  setMeasurementsChanged(data: WeatherData) {
    this.state = data
    // 这里可以控制更新的条件 利于条件控制状态的更新频率 比如温差超过5度或者某个字段变化了才通知更新 可以写自己的变化逻辑
    // if(data.temperature-this.state.temperature>=5) // 伪代码举例 这里的字段并不是数字类型 这里根据实际情况设计
    if (true) {
      this.setChanged(true)
    }
    this.measurementsChanged(data)
  }
  addObserver(observer: Observer) {
    return this.observers.push(observer)
  }

  deleteObserver(observer: Observer) {
    const idx = this.observers.findIndex((e) => e === observer)
    if (idx >= 0) {
      this.observers.splice(idx, 1)
      return true
    }
    return false
  }
  notifyObservers(data: WeatherData) {
    if (!this.changed) return // 如果没有更新则不通知
    // 遍历所有的观察者通知更新
    for (const observer of this.observers) {
      observer.update(data)
    }
    this.setChanged(false) // 通知更新后修改是否更新状态为false
  }
  // 设置是否有更新的标志
  setChanged(flag: boolean) {
    this.changed = flag
  }
}

有了气象站类后,实现观察者类

// 气温观察者
class TemperatureObserver implements Observer {
  update(data: WeatherData) {
    this.display(data)
  }
  display(data: WeatherData) {
    console.log('温度观察者:', data.temperature)
  }
}
// 湿度观察者
class HumidityObserver implements Observer {
  update(data: WeatherData) {
    this.display(data)
  }
  display(data: WeatherData) {
    console.log('湿度观察者:', data.humidity)
  }
}
// 气压观察者
class PressureObserver implements Observer {
  update(data: WeatherData) {
    this.display(data)
  }
  display(data: WeatherData) {
    console.log('气压观察者:', data.pressure)
  }
}

不同类型的布告板实现观察者接口定义各自的展示行为,必需实现update方法,然后往气象站添加观察者并通知更新

const subject = new Weather() // 创建气象站主题
// 添加观察者
subject.addObserver(new TemperatureObserver())
subject.addObserver(new HumidityObserver())
subject.addObserver(new PressureObserver())
// 气象站状态发生改变
subject.setMeasurementsChanged({
  temperature: '37度',
  humidity: '干燥',
  pressure: '101帕',
})
subject.setMeasurementsChanged({
  temperature: '38度',
  humidity: '滋润',
  pressure: '99帕',
})

控制台打印结果,两次状态改变成功通知更新

 以上就是一个观察模式的简易案例,原案例使用Java实现,自己理解后用ts改写,Vue的响应式原理也用到了观察者模式,知悉数据的变化通知更新页面

<think>我们之前已经详细介绍了观察者模式的概念、结构、优势挑战以及应用场景,并且给出了一个电商订单状态通知的实战案例。现在用户要求获取观察者模式的具体功能实现代码。根据引用[3]和引用[5],观察者模式实现方式多样,但核心设计思路一致。下面我将提供一个简洁且完整的Java实现示例,并附上详细注释。 ### 观察者模式核心代码实现 #### 1. 观察者接口(Observer) 定义观察者的响应方法规范。 ```java public interface Observer { void update(); // 当主题状态改变时,该方法被调用 } ``` #### 2. 主题接口(Subject) 定义管理观察者的方法。 ```java public interface Subject { void registerObserver(Observer o); // 注册观察者 void removeObserver(Observer o); // 移除观察者 void notifyObservers(); // 通知所有观察者 } ``` #### 3. 具体主题(ConcreteSubject) 实现主题接口,维护观察者列表和状态,并在状态改变时通知观察者。 ```java import java.util.ArrayList; import java.util.List; public class WeatherStation implements Subject { private List<Observer> observers; private float temperature; // 温度状态 public WeatherStation() { observers = new ArrayList<>(); } @Override public void registerObserver(Observer o) { observers.add(o); } @Override public void removeObserver(Observer o) { observers.remove(o); } @Override public void notifyObservers() { for (Observer o : observers) { o.update(); // 通知每个观察者 } } // 当温度变化时,更新状态并通知观察者 public void setTemperature(float temperature) { this.temperature = temperature; notifyObservers(); } public float getTemperature() { return temperature; } } ``` #### 4. 具体观察者(ConcreteObserver实现观察者接口,在更新方法中获取主题状态并执行响应操作。 ```java public class TemperatureDisplay implements Observer { private Subject weatherStation; // 持有主题的引用,用于获取状态 public TemperatureDisplay(Subject weatherStation) { this.weatherStation = weatherStation; weatherStation.registerObserver(this); // 注册自己到主题 } @Override public void update() { float temp = ((WeatherStation) weatherStation).getTemperature(); System.out.println("温度显示器: 当前温度为 " + temp + "℃"); } } ``` #### 5. 客户端使用 ```java public class WeatherApp { public static void main(String[] args) { // 创建主题(气象站) WeatherStation station = new WeatherStation(); // 创建观察者并注册到主题 TemperatureDisplay display = new TemperatureDisplay(station); // 模拟温度变化 station.setTemperature(25.6f); station.setTemperature(26.8f); } } ``` #### 运行结果 ``` 温度显示器: 当前温度为 25.6℃ 温度显示器: 当前温度为 26.8℃ ``` ### 关键点说明 1. **松耦合**:主题`WeatherStation`完全不知道观察者的具体实现,只依赖`Observer`接口[^2]。 2. **动态注册与移除**:通过`registerObserver`和`removeObserver`方法可以动态管理观察者。 3. **通知机制**:当主题状态改变(`setTemperature`被调用)时,自动触发所有观察者的更新操作。 ### 模式优化建议 - **异步通知**:若观察者响应耗时,可在`notifyObservers`中使用线程池异步调用`update`方法。 - **防止循环依赖**:避免观察者在`update`方法中修改主题状态导致递归调用。 --- §§相关问题§§ 1. 如何在观察者模式实现**按需通知**(例如只通知关注特定事件的观察者)? 2. 观察者模式与**责任链模式**在事件处理上有何本质区别? 3. 如何解决观察者模式中**跨线程状态同步**的问题? 4. Java中`PropertyChangeListener`如何实现观察者模式? 5. 观察者模式在**GUI框架**(如Swing/JavaFX)中的具体应用是怎样的?[^4] </think>### 观察者模式核心实现代码 以下提供三种不同语言的观察者模式实现(含详细注释),均遵循标准结构并优化实际开发细节: #### 一、Java 实现(强类型接口方案) ```java // 1. 观察者接口(抽象订阅者) interface Observer { void update(String eventData); // 事件响应方法 } // 2. 主题接口(被观察对象) abstract class Subject { private final List<Observer> observers = new ArrayList<>(); public void attach(Observer o) { observers.add(o); // 注册观察者 } public void notifyObservers(String event) { observers.forEach(obs -> obs.update(event)); // 广播通知 } } // 3. 具体主题(气象站) class WeatherStation extends Subject { private float temperature; public void setTemperature(float temp) { this.temperature = temp; notifyObservers("温度更新: " + temp + "℃"); // 状态变化触发通知 } } // 4. 具体观察者(手机天气App) class PhoneDisplay implements Observer { @Override public void update(String event) { System.out.println("[手机端] 收到气象数据: " + event); } } // 客户端使用 public class Client { public static void main(String[] args) { WeatherStation station = new WeatherStation(); PhoneDisplay phone = new PhoneDisplay(); station.attach(phone); // 绑定观察关系 station.setTemperature(28.5f); // 触发通知 // 输出: [手机端] 收到气象数据: 温度更新: 28.5℃ } } ``` #### 二、Python 实现(动态类型方案) ```python # 1. 主题类(被观察者) class Subject: def __init__(self): self._observers = [] # 观察者列表 def attach(self, observer): self._observers.append(observer) def notify(self, event): for obs in self._observers: obs(event) # Python支持函数作为观察者 # 2. 具体业务对象 class StockMarket: def __init__(self): self.subject = Subject() self.price = 100.0 def set_price(self, new_price): self.price = new_price self.subject.notify(f"股价波动: {self.price}$") # 触发通知 # 3. 观察者实现(函数式/类式) def sms_alert(event): print(f"[短信提醒] {event}") class EmailNotifier: def __call__(self, event): print(f"[邮件通知] {event}") # 客户端使用 if __name__ == "__main__": market = StockMarket() market.subject.attach(sms_alert) # 绑定函数观察者 market.subject.attach(EmailNotifier()) # 绑定类实例观察者 market.set_price(105.3) # 输出: # [短信提醒] 股价波动: 105.3$ # [邮件通知] 股价波动: 105.3$ ``` #### 三、TypeScript 实现(前端场景优化) ```typescript // 1. 观察者接口 interface Observer { onEvent(data: string): void; } // 2. 可观察对象(支持自动注销) class Observable { private observers: Observer[] = []; attach(obs: Observer): void { this.observers.push(obs); } detach(obs: Observer): void { this.observers = this.observers.filter(o => o !== obs); } notify(event: string): void { this.observers.forEach(obs => obs.onEvent(event)); } } // 3. 具体主题(UI按钮) class DownloadButton extends Observable { click(): void { this.notify("用户点击下载按钮"); // 触发UI事件 } } // 4. 具体观察者(日志组件) class Logger implements Observer { onEvent(data: string): void { console.log(`[日志系统] ${new Date().toISOString()} - ${data}`); } } // 客户端使用 const button = new DownloadButton(); const logger = new Logger(); button.attach(logger); button.click(); // 输出: [日志系统] 2023-08-15T07:30:00.000Z - 用户点击下载按钮 ``` ### 关键实现要点 1. **解耦核心** - 主题仅通过抽象接口`Observer`与观察者交互 - 新增观察者无需修改主题代码 [^2][^3] 2. **通知控制** ```java // 过滤特定事件观察者(Java示例扩展) interface Observer { void update(String eventType, String data); // 增加事件类型参数 } void notifyByType(String eventType, String data) { observers.stream() .filter(obs -> obs.supports(eventType)) // 自定义过滤逻辑 .forEach(obs -> obs.update(data)); } ``` 3. **防止内存泄漏** ```typescript // 前端组件卸载时解除订阅(TS示例) class ReactComponent { componentDidMount() { service.attach(this.handler); } componentWillUnmount() { service.detach(this.handler); // 显式释放引用 } } ``` ### 性能优化技巧 1. **批量通知** ```python # Python 批量更新示例 class Subject: def __init__(self): self._pending_events = [] def queue_event(self, event): self._pending_events.append(event) def flush_events(self): # 手动触发批量通知 for event in self._pending_events: self.notify(event) self._pending_events.clear() ``` 2. **异步执行**(Java示例) ```java void notifyAsync(String event) { Executors.newCachedThreadPool().submit(() -> { observers.parallelStream().forEach(obs -> obs.update(event)); }); } ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值