观察者模式

设计模式之观察者模式

有时候在京东看到一些电子产品非常想买,但奈何没钱啊!只能每隔几天就上京东看看这件商品有没有降价,有几次都错过了优惠活动!这让我悔恨不已啊!!!后来才发现京东有个“关注商品”的功能,商品一旦降价就会立马给我推送信息!我居然一直没发现!自从知道有这个功能后,我……

穷啊

其实设计模式里面也有一种模式类似于这种机制,那就是观察者模式。从此以后我再也没有错过商品的降价信息,

1.定义

观察者模式(Observer Pattern)建立一种对象与对象之间的依赖关系,一个对象发生改变时将自动通知其他对象,其他对象将相应做出反应。在此,发生改变的对象称为主题或者观察目标,而被通知的对象称为观察者,一个观察目标可以对应多个观察者,而且这些观察者之间没有相互联系,可以根据需要增加和删除观察者,使得系统更易于扩展。

观察者模式包含以下角色:

  1. 主题
  2. 具体主题
  3. 观察者
  4. 具体观察者

2.实例说明

以点外卖的例子来说,我们通常在饿了么、美团这些APP上点外卖,我们在APP下单后,相应餐厅的APP此时会提醒接单,与此同时配送员的手机APP会立马提醒抢单。这个时候,每个订单都是具体主题,而餐厅及配送员就是具体观察者,下面用代码来实现。

2.1 Observer接口

观察者都需要实现这个接口,当数据更新后,主题调用update()方法,把数据传送给观察者。

1
2
3
public interface Observer {
public void update(String food, String address, String phone);
}

2.2 Subject接口

主题最起码需要实现三个方法,registerObserver()方法用于观察者订阅主题,removeObserver()用于观察者取消订阅主题,notifyObserver()用于推送最新数据给观察者。

1
2
3
4
5
public interface Subject {
public void registerObserver(Observer observer);
public void removeObserver(Observer observer);
public void notifyObserver();
}

2.3 实现Observer接口

在构造的时候就订阅主题。

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
46
47
//餐厅
public class Restaurant implements Observer {
private String food;

public Restaurant(Subject subject) {
subject.registerObserver(this);
}

@Override
public void update(String food, String address, String phone) {
this.food = food;
display();
}

public void display(){
System.out.println("餐厅,有订单了!用户点的菜是:" + this.food + ",是否接单?");
}

public void unlink(Subject subject){
subject.removeObserver(this);
}
}

//配送员
public class DeliverGuy implements Observer {
private String address;
private String phone;

public DeliverGuy(Subject subject) {
subject.registerObserver(this);
}

@Override
public void update(String food, String address, String phone) {
this.address = address;
this.phone = phone;
display();
}

public void display() {
System.out.println("外卖员,有订单了!地址是:" + this.address + ",手机号码是:" + this.phone + ",是否接单?");
}

public void unlink(Subject subject) {
subject.removeObserver(this);
}
}

2.4 实现Subject接口

把所有订阅该主题的观察者对象放进一个ArrayList里,有数据更新的时候逐个调用它们的update()方法。

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
public class Application implements Subject {
private ArrayList observers;
private String food;
private String address;
private String phone;

public Application() {
observers = new ArrayList();
}

@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}

@Override
public void removeObserver(Observer observer) {
int i = observers.indexOf(observer);
if (i >= 0) {
observers.remove(i);
}
}

@Override
public void notifyObserver() {
for (int i = 0; i < observers.size(); i++) {
Observer observer = (Observer) observers.get(i);
observer.update(food, address, phone);
}
}

public void comeNewOrder(String food, String address, String phone) {
this.food = food;
this.address = address;
this.phone = phone;
notifyObserver();
}
}

3.测试

测试代码和测试结果如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Test {
public static void main(String args[]) {
Application application = new Application();
Restaurant restaurant = new Restaurant(application);
DeliverGuy deliverGuy = new DeliverGuy(application);

application.comeNewOrder("番茄炒蛋饭", "富华西路106号", "12345678910");

deliverGuy.unlink(application); //该配送员送完上一单不想再送了,不再接受接单消息
application.comeNewOrder("土豆牛肉饭", "富华东路178号", "18910245613");

restaurant.unlink(application); //餐厅打烊了,不再接受订单消息
application.comeNewOrder("番茄炒蛋饭", "富华西路123号", "17654245645");
}
}

测试结果

4.优缺点

4.1 优点

1.观察者模式可以实现表示层和数据逻辑层的分离,并定义了稳定的消息更新传递机制,抽象了更新接口,使得可以有各种各样不同的表示层作为具体观察者角色。
2.观察者模式符合“开闭原则”的要求。

4.2 缺点

1.如果一个主题有很多直接和间接的观察者的话,所有的观察者都通知会花费很多时间。
2.观察者模式没有相应的机制让观察者知道所观察的主题是怎么发生变化的,而仅仅只是知道主题发生了变化。

5.适用场景

观察者模式在软件开发中应用非常广泛,就比如我上面提到的京东降价消息,或者某团队战斗游戏中某队友牺牲将给所有成员提示等等,凡是涉及到一对一或者一对多的对象交互场景都可以使用观察者模式。