简介
观察者模式(Observer Pattern)是一种行为型模式。它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
观察者模式使用三个类Subject、Observer和Client。Subject对象带有绑定观察者到Client对象和从Client对象解绑观察者的方法。我们创建Subject类、Observer抽象类和扩展了抽象类Observer的实体类。
作用
- 一个对象状态更新,其他依赖对象收到通知和自动更新的机制。
- 实现模块化分离,实现主题与观察者交互对象之间的松耦合。
1)观察者定义了对象之间一对多的关系。
2)被观察者(主题)用一个共同的接口来更新观察者。
3)观察者和被观察者用松耦合方式结合,被观察者不知道观察者的细节,只知道观察者实现了观察者接口。
实现步骤
- 创建观察者observer基础接口,包含主题和更新方法
- 创建主题subject抽象类,包含observer列表以及添加和删除方法
- 创建具体的主题类,实现通知方法,发布通知时轮询通知全部观察者
- 创建多个具体观察者,与主题关联,并实现自己的更新方法
- 客户调用时先声明主题,再将观察者分别添加到主题,当主题发布通知时,观察者自动更新
UML
Java代码
观察者接口
| // ObserverAPI.java 观察者接口,Java 9已经默认支持Observer接口 |
| // 这里避免冲突采取ObserverAPI命名 |
| public interface ObserverAPI { |
| public Subject subject = null; |
| public void update(String content); |
| } |
具体观察者
| // ConcreteObserver.java 具体的观察者实现类,也可以看成订阅者,关联对应的主题类。 |
| // 不同的观察者也可以对应多个主题 |
| public class ConcreteObserver implements ObserverAPI { |
| |
| public Subject subject; |
| |
| // 给观察者绑定主题,同时把观察者添加到主题列表 |
| public ConcreteObserver(Subject subject) { |
| this.subject = subject; |
| this.subject.register((ObserverAPI) this); |
| } |
| |
| // 观察者发出更新通知,不用单独告诉订阅者,由订阅者自行监听 |
| public void update(String content) { |
| System.out.println(String.format("%s::update() [subject.name = %s content = %s]", |
| this.getClass().getName(), |
| this.subject.getName(), content)); |
| } |
| } |
| // ConcreteObserver2.java 具体的观察者实现类,也可以看成订阅者,关联对应的主题类。 |
| // 不同的观察者可以对应不同的主题。 |
| public class ConcreteObserver2 implements ObserverAPI { |
| |
| // 这里没有在构造器就绑定某个主题,而是从客户角度去注册观察者 |
| public ConcreteObserver2() { |
| } |
| |
| // 观察者发出更新通知,观察者自行监听 |
| public void update(String content) { |
| System.out.println(String.format("%s::update() [content = %s]", |
| this.getClass().getName(), content)); |
| } |
| } |
抽象主题类
| // Subject.java 定义抽象主题类或者接口,供具体主题类继承 |
| public abstract class Subject { |
| private String name; |
| // protected Set<ObserverAPI> observers = new HashSet<>(); |
| protected List<ObserverAPI> observers = new ArrayList<>(); |
| |
| public String getName() { |
| return name; |
| } |
| |
| public void setName(String name) { |
| this.name = name; |
| } |
| |
| public void register(ObserverAPI observer) { |
| System.out.println(this.getClass().getName() + "::register() [observer = " + observer.getClass().getSimpleName() + "]"); |
| observers.add(observer); |
| } |
| |
| public void remove(ObserverAPI observer) { |
| observers.remove(observer); |
| } |
| |
| // 通知由具体类来实现逻辑 |
| public abstract void notify(String name); |
| } |
具体主题类
| // ConcreteSubject.java 观察者主题类,也是发布者,重写具体的通知方法。不同主题可以关联不同的观察者。 |
| public class ConcreteSubject extends Subject { |
| |
| public ConcreteSubject(String name) { |
| this.setName(name); |
| } |
| |
| // 不同的主题类有自己的通知方法,批量通知绑定的观察者 |
| @Override |
| public void notify(String content) { |
| System.out.println(this.getClass().getName() + "::notify() [content = " + content + "]"); |
| for (Object observer : this.observers) { |
| ((ObserverAPI) observer).update(content); |
| } |
| |
| } |
| |
| } |
测试调用
| /** |
| * 观察者模式应用非常广泛,主要是观察者提前绑定到发布者 |
| * 当发布者发布消息时,批量广播通知,而无需逐一通知 |
| * 观察者监听到消息后自己决定采取哪一种行为 |
| */ |
| |
| // 定义一个主题,也就是发布者 |
| Subject concreteSubject = new ConcreteSubject("subject1"); |
| // 再声明观察者,通过构造器注册到主题上 |
| ObserverAPI observer1 = new ConcreteObserver(concreteSubject); |
| |
| // 也可以单独给主题注册一个新的观察者 |
| concreteSubject.register(new ConcreteObserver2()); |
| // 可以移除观察者对象,可以打开注释试下 |
| // concreteSubject.remove(observer1); |
| |
| // 主题开始发布新通知,各观察者自动更新 |
| concreteSubject.notify("hello, this is broadcast."); |
| |
Python代码
观察者接口
| # ObserverAPI.py 观察者抽象父类,定义一些公共方法 |
| class ObserverAPI: |
| |
| def __init__(self, name): |
| self.name = name |
| |
| # 观察者发出更新通知,观察者自行监听 |
| def update(self, content): |
| print(self.__class__.__name__ + '::update() [content = ' + content + ']') |
| |
| def set_name(self, name): |
| self.name = name |
具体观察者
| # ConcreteObserver.py 具体的观察者实现类,也可以看成订阅者,关联对应的主题类。 |
| # 不同的观察者也可以对应多个主题 |
| from src.ObserverAPI import ObserverAPI |
| |
| # 具体的观察者实现类,也可以看成订阅者,关联对应的主题类。 |
| # 不同的观察者也可以对应多个主题 |
| |
| |
| class ConcreteObserver(ObserverAPI): |
| # 给观察者绑定主题,同时把观察者添加到主题列表 |
| def __init__(self, subject, name): |
| ObserverAPI.__init__(self, name) |
| |
| # python3支持的父类调用 |
| # super(ConcreteObserver, self).__init__(name) |
| # super().__init__(name) |
| |
| self.subject = subject |
| subject.register(self) |
| |
| # 观察者发出更新通知,不用单独告诉订阅者,由订阅者自行监听 |
| def update(self, content): |
| print(self.__class__.__name__ + '::update() [subject.name = ' + |
| self.subject.name + ' content = ' + content + ']') |
| # ConcreteObserver2.py 具体的观察者实现类,也可以看成订阅者,关联对应的主题类。 |
| # 不同的观察者可以对应不同的主题。 |
| from src.ObserverAPI import ObserverAPI |
| |
| |
| # 具体的观察者实现类,也可以看成订阅者,关联对应的主题类。 |
| # 不同的观察者可以对应不同的主题。 |
| class ConcreteObserver2(ObserverAPI): |
| # 这里没有在构造器就绑定某个主题,而是从客户角度去注册观察者 |
| # 观察者发出更新通知,观察者自行监听 |
| |
| # def update(self, content): |
| # print(self.__class__.__name__ + '::update() [content = ' + content +']') |
| |
| pass |
抽象主题类
| # Subject.py 定义抽象主题类或者接口,供具体主题类继承 |
| class Subject: |
| |
| def __init__(self, name): |
| self.name = name |
| self.observers = [] |
| |
| def get_name(self): |
| return self.name |
| |
| def set_name(self, name): |
| self.name = name |
| |
| def register(self, observer): |
| print(self.__class__.__name__ + '::register() [observer = ' + |
| observer.__class__.__name__ + ']') |
| |
| self.observers.append(observer) |
| |
| def remove(self, observer): |
| self.observers.remove(observer) |
| |
| # 通知由具体类来实现逻辑 |
| def notify(self, name): |
| pass |
具体主题类
| // ConcreteSubject.py 观察者主题类,也是发布者,重写具体的通知方法。不同主题可以关联不同的观察者。 |
| from src.Subject import Subject |
| |
| |
| # 观察者主题类,也是发布者,重写具体的通知方法。不同主题可以关联不同的观察者。 |
| class ConcreteSubject(Subject): |
| # 不同的主题类有自己的通知方法,批量通知绑定的观察者 |
| def notify(self, content): |
| print(self.__class__.__name__ + '::notify() [content = ' + content + |
| ']') |
| for observer in self.observers: |
| observer.update(content) |
测试调用
| import sys |
| import os |
| |
| os_path = os.getcwd() |
| sys.path.append(os_path) |
| |
| from src.ConcreteSubject import ConcreteSubject |
| from src.ConcreteObserver import ConcreteObserver |
| from src.ConcreteObserver2 import ConcreteObserver2 |
| |
| |
| def test(): |
| ''' |
| * 观察者模式应用非常广泛,主要是观察者提前绑定到发布者 |
| * 当发布者发布消息时,批量广播通知,而无需逐一通知 |
| * 观察者监听到消息后自己决定采取哪一种行为 |
| ''' |
| |
| # 定义一个主题,也就是发布者 |
| concrete_subject = ConcreteSubject('subject1') |
| # 再声明观察者,通过构造器注册到主题上 |
| observer1 = ConcreteObserver(concrete_subject, 'observer1') |
| # 也可以单独给主题注册一个新的观察者 |
| observer2 = ConcreteObserver2('observer2') |
| concrete_subject.register(observer2) |
| # 可以移除观察者对象 |
| # concrete_subject.remove(observer1) |
| |
| # 主题开始发布新通知,各观察者自动更新 |
| concrete_subject.notify('hello, this is broadcast.') |
| |
| |
| if __name__ == '__main__': |
| print(__file__) |
| print("test start:") |
| test() |
| |
更多语言版本
不同语言实现设计模式:https://github.com/microwind/design-pattern