前言
今天,学习观察者模式。
一、概述
1.1 引入
观察者模式,又称为发布订阅模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当这个主题对象在状态变化时,会通知所有的观察者对象,使他们能够自动更新自己。
ok,观察者模式又称为发布-订阅模式。
既有观察者,也有被观察者。
当被观察者产生某种行为之后,就会通知观察者执行某种操作。
举几个场景:
- 1、当保安 (观察者) 发现店里有人 (被观察者) 偷东西(事件),就会出手干预;
- 2、当用户(观察者)关注了潇雷公众号(被观察者),如果有推送消息(事件),就会接收到;
- 3、当老婆(观察者)发现老公(被观察者)发工资(事件),可能就会开始上交模式;
- ...
1.1 结构
OK, 观察者相对来说容易列举的场景会有很多,也很容易理解,现在我们要再次定义下观察者模式拥有的角色。
上面可能已经发现了,至少存在两个角色-观察者与被观察者。
对应到面向对象中,因为要解耦,所以又抽象出,具体观察者和具体被观察者,四个对象啦。
-
Subject 被观察者:
- 抽象被观察者,定义了被观察者的事件(方法),它必须要能够动态的新增和取消观察者。里面维护了一个所有观察者对象的集合。
-
Observer 观察者:
- 抽象观察者,是观察者的抽象类,定义了一个接收方法,当被观察者发生变化时,就会执行这个观察者方法
-
ConcrectSubject:
- 具体被观察者:实现具体被观察者的事件方法。
-
ConcrectObserver:
- 具体观察者:实现抽象观察者定义的接收方法。
二、案例实现
2.1 案例引入
这边也举个简单的场景。
农夫家里养了很多动物,鸡、鸭、猪等
每次到饭点的时候,农夫就会进入围栏里喊它们,动物一观察到这个举动就立马跑过来。
抽象成角色模型,大概是下面这种。
2.2 代码引入
抽象观察者:
csharp
public interface Observer {
void subscript();
}
具体观察者:
typescript
public class AnimalObserver implements Observer{
private String name;
public AnimalObserver(String name){
this.name = name;
}
@Override
public void subscript() {
System.out.println(name+"看到主人进来,跑过去干饭");
}
}
被观察者:
csharp
public interface Subject {
// 添加观察者
void addObserver(Observer observer);
// 删除观察者
void deleteObserver(Observer observer);
// 通知方法
void notifyObserver();
}
具体被观察者:
typescript
public class OwnerSubject implements Subject{
private List<Observer> observers = new ArrayList<>();
@Override
public void addObserver(Observer observer) {
observers.add(observer);
}
@Override
public void deleteObserver(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObserver() {
for (Observer observer : observers) {
observer.subscript();
}
}
}
客户端测试
ini
public class Client {
public static void main(String[] args) {
OwnerSubject ownerSubject = new OwnerSubject();
AnimalObserver observer = new AnimalObserver("猪猪");
AnimalObserver observer2 = new AnimalObserver("母鸡");
AnimalObserver observer3 = new AnimalObserver("鸭子");
ownerSubject.addObserver(observer);
ownerSubject.addObserver(observer2);
ownerSubject.addObserver(observer3);
// 主人进来这个动作,被这些订阅者观察到
ownerSubject.notifyObserver();
}
}
打印输出:
2.3 spring Event 引入
其实观察者模式,跟发布-订阅模型非常相似。
很多中间件都提供了这种模式,消费者就是观察者的角色。而Spring 中我们常常想实现观察者模式,不会说自己来手写,更多的是采用 Spring Event 基于事件解耦的方式来完成。
Spring Event 将发布者和消费者分离开,实现代码的解耦,让程序的代码更加职责明确清晰。同时,Spring Event 支持异步事件处理,提高系统的并发处理能力。
对应三个角色:
-
事件(Event):事件是个普通的对象,封装关于事件的信息
-
事件发布者:触发发布事件
-
事务监听者:监听负责响应特定类型事件的组件,并定义了事件发生时需要执行的逻辑。
那如何用 Spring 来实现我们上述的这个例子呢?
- 发布者:
typescript
@Service
public class OwnerPublish {
@Autowired
private ApplicationContext applicationContext;
public void publish(){
applicationContext.publishEvent(new FoodEvent("来干饭!"));
}
}
- 事件:
scala
public class FoodEvent extends ApplicationEvent {
public FoodEvent(Object source) {
super(source);
}
}
- 消费者
csharp
@Component
public class AnimalEventListener {
@EventListener(FoodEvent.class)
public void onApplicationEvent(FoodEvent event){
Object source = event.getSource();
System.out.println("猪猪:"+source);
}
}
less
@Component
public class Animal2EventListener {
@EventListener(FoodEvent.class)
@Async
public void onApplicationEvent(FoodEvent event){
Object source = event.getSource();
System.out.println("大鹅:"+source);
}
}
客户端:
typescript
@Autowired
OwnerPublish ownerPublishl;
@Test
public void sendMsg(){
ownerPublishl.publish();
}
发布订阅模式与观察者模式还是有区别的。
-
观察者模式中,观察者是知道有哪些被观察者的,就像小动物知道有主人这个记录。
-
而发布订阅模式中,发布者和订阅者,是通过事件解耦的,都不需要知道对方的存在。
总结
观察者模式实现了解耦,定义了一种一对多的依赖关系,多个观察者同时监听者某个对象,当对象触发某动作后,多个观察者就会同时执行某种操作。同时被观察者必须要维护有哪些观察者的列表,这是与发布-订阅模式的区别所在,所以类似微信公众号发文,维护了一个粉丝列表,这些人可以同时接收到对应信息。
何时使用观察者模式?
- 想抽离业务解耦代码的时候
- 当多个对象依赖同一个对象方法的时候
- 还可以通过异步处理方法,提高程序的性能。