观察者模式

观察者模式

观察者模式属于行为型模式。在程序设计中,观察者模式通常由两个对象组成:观察者和被观察者。当被观察者状态发生改变时,它会通知所有的观察者对象,使他们能够及时做出响应,所以也被称作“发布-订阅模式”。

组成

  • 抽象主题(Subject):定义了一个接口,包含了注册观察者、删除观察者、通知观察者等方法。
  • 具体主题(ConcreteSubject):实现了抽象被观察者接口,维护了一个观察者列表,并在状态发生改变时通知所有注册的观察者。
  • 抽象观察者(Observer):定义了一个接口,包含了更新状态的方法。
  • 具体观察者(ConcreteObserver):实现了抽象观察者接口,存储了需要观察的被观察者对象,并在被观察者状态发生改变时进行相应的处理。

例子

股票充当主题,投资者充当观察者,但股票价格发生变动时,通知所有的观察者

image-20231229141100830

抽象主题

1
2
3
4
5
6
public abstract class Subject {
public abstract void addObserver(Observer o);
public abstract void delObserver(Observer o);
public abstract void notifyObserver();
}

股票:具体主题

持有一个观察者的集合,当股票价格变动时,调用notifyobserver方法通知所有观察者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public class Stock extends Subject{

private ArrayList<Observer> list = new ArrayList<>();
private String name;
private double price;

public Stock(String name,double price){
this.name = name;
this.price = price;
}

@Override
public void addObserver(Observer o) {
list.add(o);
}

@Override
public void delObserver(Observer o) {
list.remove(o);
}

@Override
public void notifyObserver() {
for(Observer o : list){
o.respond(this);
}
}

public void setPrice(double price){
double rate = Math.abs(price-this.price)/this.price;
this.price = price;
if(rate >= 0.05){
notifyObserver();
}
}

public String getName(){
return this.name;
}

public double getPrice(){
return this.price;
}

}

抽象观察者

1
2
3
public interface Observer {
public void respond(Stock stock);
}

投资者:具体观察者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Investor implements  Observer{
private String name;
public Investor(String name){
this.name = name;
}

@Override
public void respond(Stock stock) {
System.out.println("提示股民:" + this.name);
System.out.println("股票"+ stock.getName()+"价格变化超过5%");
System.out.println("当前的价格为:"+ stock.getPrice());
}
}

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Client {
public static void main(String[] args) {
Stock stock1 = new Stock("苹果",100);
Stock stock2 = new Stock("华为",80);

Investor investor1 = new Investor("盖伦");
Investor investor2 = new Investor("赵信");

stock1.addObserver(investor1);//盖伦购买苹果股票
stock2.addObserver(investor1);//盖伦购买华为股票
stock1.addObserver(investor2);//赵信购买苹果股票
stock2.addObserver(investor2);//赵信购买华为股票

stock1.setPrice(102);
stock2.setPrice(100); //变动超过5%,通知所有观察者
}
}

image-20231229141830519

优点:

  • 被观察者和观察者对象之间不需要知道对方的具体实现,只需要知道对方的接口,避免了紧耦合的关系。
  • 由于被观察者对象并不关心具体的观察者是谁,所以在程序运行的过程中,可以动态地增加或者删除观察者对象,增加了灵活性。
  • 符合开闭原则,当需要添加新的观察者时,只需要添加一个实现观察者接口的类,而不需要修改被观察者对象的代码。

缺点:

当观察者没有被正确移除时,可能会导致内存泄漏的问题。

实现观察者模式,需要定义多个接口和类,增加了程序的复杂度。

在某些情况下,被观察者和观察者对象之间可能出现循环依赖的问题。

推和拉

主题对象向观察者推送主题的详细信息,不管观察者是否需要,推送的信息通常是主题对象的全部或部分数据。

主题对象在通知观察者的时候,只传递少量信息。如果观察者需要更具体的信息,由观察者主动到主题对象中获取,相当于是观察者从主题对象中拉数据。一般这种模型的实现中,会把主题对象自身通过update()方法传递给观察者,这样在观察者需要获取数据的时候,就可以通过这个引用来获取了。

应用场景

生活场景
拍卖的时候,拍卖师是观察者,价格是被观察者。拍卖师观察最高标价,然后通知给其他竞价者竞价。

共享单车:共享单车是被观察者对象,用户是观察者对象。当有新的单车被放置或被租用时,系统会发送给用户通知。

微信公众号:微信公众号是被观察者对象,粉丝是观察者对象。当公众号发布了新的文章或消息时,系统会发送消息给关注该公众号的粉丝。

程序场景

当一个对象的状态发生改变时,需要通知多个对象做出相应的响应。例如,王者荣耀更新前,会通知所有用户要更新的时间。

当很多对象同时对某一个主题感兴趣时,可以采用观察者模式实现发布-订阅模式。例如,生产者发送消息到消息队列中,并通知所有订阅此队列的消费者进行消费。

数据库开发中,当数据库表中的数据发生变化时,需要通知相关的模块进行更新或其他操作。例如,当用户更新了数据库中的某个记录时,就可以通过观察者模式通知所有注册的监听器进行响应。


观察者模式
http://example.com/2023/10/15/计算机基础/设计模式/13-观察者模式/
作者
PALE13
发布于
2023年10月15日
许可协议