咸鱼开发修炼之路

设计模式-观察者模式

简介

观察者模式又称为发布/订阅(Publish/Subscribe)模式,观察者模式中有主题(Subject)和观察者(Observer),分别对应报社和订阅用户(你).
观察者模式定义了对象之间的一对多的依赖关系,这样,当”一”的一方状态发生变化时,它所依赖的”多”的一方都会收到通知并且自动更新。
dcd7af5d.png

观察者模式所涉及的角色有:

  • 抽象主题(Subject)角色:即抽象被观察者(Observable)角色。抽象主题角色把所有对观察者对象的引用保存在一个聚集(比如ArrayList对象)里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。
  • 具体主题(ConcreteSubject)角色:即具体被观察者(Concrete Observable)角色。将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。
  • 抽象观察者(Observer)角色:为所有的具体观察者定义一个接口,在得到主题的通知时更新自己,这个接口叫做更新接口。
  • 具体观察者(ConcreteObserver)角色:存储与主题的状态自恰的状态。具体观察者角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态 像协调。如果需要,具体观察者角色可以保持一个指向具体主题对象的引用。4673159a.png

简单实现

一个简单例子,教师布置作业。
7be01022.png

抽象主题(Subject)角色:

1
2
3
4
5
6
7
8
9
10
/**
* 即被观察者(Observable)
*/
public interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObserver();
}

具体主题(ConcreteSubject)角色:
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
public class TeacherSubject implements Subject {
List<Observer> observers = new ArrayList<>();
String info ;
@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObserver() {
observers.forEach(observer -> observer.update(info));
}
public void setHomework(String info){
this.info=info;
System.out.println("今天的作业是"+info);
this.notifyObserver();
}
}

抽象观察者(Observer)角色:
1
2
3
public interface Observer {
void update(String msg);
}

具体观察者(ConcreteObserver)角色:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class StudentObserver implements Observer {
private String name ;
public StudentObserver() {}
public StudentObserver(String name) {
this.name = name ;
}
@Override
public void update(String msg) {
System.out.println(name + ":" +msg);
}
}

java内置的观察者模式

在java.util包中包含有基本的Observer接口和Observable抽象类.

使对象变为观察者

实现观察者接口(java.util.Observer),然后调用Observable对象的addObserver()方法.不想再当观察者时,调用deleteObserver()就可以了

被观察者(主题)发出通知

  • 第一步:先调用setChanged()方法,标识状态已经改变的事实.
  • 第二步:调用notifyObservers()方法或者notifyObservers(Object arg),这就牵扯到推(push)和拉(pull)的方式传送数据.
    如果想用push的方式”推”数据给观察者,可以把数据当做数据对象传送给notifyObservers(Object arg)方法,其中的arg可以为任意对象,意思是你可以将任意对象传送给每一个观察者.
    如果调用不带参数的notifyObserver()方法,则意味着你要使用pull的方式去主题对象中”拉”来所需要的数据.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    public class TeacherSubject extends Observable {
    private String info;
    public void setHomework(String info) {
    this.info = info;
    System.out.println("老师布置" + info);
    setChanged();
    notifyObservers(info);
    }
    public String getInfo() {
    return info;
    }
    }

观察者如何接收通知

观察者只需要实现一个update(Observable o,Object arg)方法,第一个参数o,是指定通知是由哪个主题下达的,第二个参数arg就是上面notifyObserver(Object arg)里传入的数据,如果不传该值,arg为null

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class StudentObserver implements Observer {
private String name ;
public StudentObserver() {
this.name = "tom" ;
}
public StudentObserver(String name) {
this.name = name ;
}
@Override
public void update(Observable o, Object arg) {
System.out.println(name + ":开始做" + arg);
}
}

总结

观察者模式提供了一种对象设计,让主题和观察者之间耦合度降得很低(大部分设计模式都是为了解耦)。
关于观察者的一切,主题只知道观察者实现了Observer接口,并不需要观察者具体的类是谁,做了什么或者其他细节。
这样的话,由于松耦合,改变主题或者观察者其中一方,并不会影响另一方,只要他们之间的接口仍被遵守,就可以自由地改变它。