观察者模式Observer -- 深入理解
一、what is 观察者模式?
观察者模式又称为发布订阅模式,它定义了一种一对多的依赖关系,一个被观察者对象会被多个观察者同时监视,当被观察者的状态发生改变,会通知所有观察者,并让其作出相应动作。
这种关系和数学里面的函数类似:比如y=2x,当x=1,y=2;x=2,y=4.....;x是自变量,y是因变量。x就对应被观察者,y就对应观察者(这时只有一个观察者,实际生活中或者开发中是多个)。
简单情形:有A、B、C、D等四个独立的对象,其中B、C、D这三个对象想在A对象发生改变的第一时间知道这种改变,以便做出相应的响应或者对策。
当然可以有多个观察者,多个被观察者。观察者与被观察者也不是对立的,一个对象可以观察其他对象,同是也可以被其他对象观察,即一个对象既是观察者又是被观察者。
二、观察者模式实例
先看个简单的实例:
public interface Observer {
public void update();
}
public class Subject {
//观察者数组
private Vector<Observer> observerVector = new Vector<>();
//增加一个观察者
public void addObserver(Observer observer) {
this.observerVector.add(observer);
}
//删除一个观察者
public void deleteObserver(Observer observer) {
this.observerVector.remove(observer);
}
//通知所有观察者
public void notifyObserver() {
for(Observer observer : this.observerVector) {
observer.update();
}
}
}
public class ConcreteSubject extends Subject {
//具体业务
public void doSomething() {
//...
super.notifyObserver();
}
}
public class ConcreteObserver implements Observer {
@Override
public void update() {
System.out.println("收到消息,进行处理");
}
}
public class Client {
public static void main(String[] args) {
//创建一个主题
ConcreteSubject subject = new ConcreteSubject();
//定义一个观察者
Observer observer = new ConcreteObserver();
//观察
subject.addObserver(observer);
//开始活动
subject.doSomething();
}
}
三、使用jdk实现观察者模式
上面的例子是自己写的Observer类,自己实现的添加观察者、删除观察者方法,实际写的时候不需要自己去写,JDK已经为我们提供了观察者模式相关的类及方法。
分别是:
- java.util.Observable ——被观察者,程序中的被观察者类,需要继承这个类。
- java.util.Observer——观察者,是接口。程序中的观察者类,需要实现这个接口中的update()方法。
具体的实例如下:
被观察者,继承Observable :
import java.util.Observable;
public class NumObservable extends Observable {
int data = 0;
public void setData(int i) {
data = i;
setChanged(); //标记此 Observable对象为已改变的对象
notifyObservers(); //通知所有观察者
}
}
观察者1,实现Observer接口:
import java.util.Observable;
import java.util.Observer;
public class NumObserver implements Observer {
@Override
public void update(Observable o, Object arg) { //有被观察者发生变化,自动调用对应观察者的update方法
NumObservable observable = (NumObservable) o; //获取被观察者对象
System.out.println("Data has changed to " + observable.data);
}
}
观察者2:
public class NumObserver_01 implements Observer {
@Override
public void update(Observable o, Object arg) {
NumObservable observable = (NumObservable) o;
Console.log("观察者2——Data has changed to " + observable.data);
}
}
Client客户端:
public class Client {
public static void main(String[] args) {
NumObservable number = new NumObservable(); //被观察者对象
number.addObserver(new NumObserver()); //给number这个被观察者添加观察者(当然可以有多个观察者)
number.addObserver(new NumObserver_01());
number.setData(1);
number.setData(2);
number.setData(3);
}
}
观察者2——Data has changed to 1
Data has changed to 1
观察者2——Data has changed to 2
Data has changed to 2
观察者2——Data has changed to 3
Data has changed to 3
四、既是观察者又是被观察者
前面说过,一个对象可以既是观察者又是被观察者。
import java.util.Observable;
import java.util.Observer;
public class NotOnlyObservableButAlsoObserver_A extends Observable implements Observer {
@Override
public void update(Observable o, Object arg) {
NotOnlyObservableButAlsoObserver_B b = (NotOnlyObservableButAlsoObserver_B) o; //获取被观察者对象
System.out.println("A监听到B数据变化:" + b.data);
setChanged();
notifyObservers(); //自己观察到数据变化,通知自己的观察者
}
}
public class NotOnlyObservableButAlsoObserver_B extends Observable implements Observer {
int data = 0;
public void setData (int i) {
data = i;
setChanged(); //标记此 Observable对象为已改变的对象
notifyObservers(); //通知所有观察者
}
@Override
public void update(Observable arg0, Object arg1) {
System.out.println("同时,A反过来通知B自己的变化");
}
}
public class Client {
public static void main(String[] args) {
NotOnlyObservableButAlsoObserver_A ooa = new NotOnlyObservableButAlsoObserver_A();
NotOnlyObservableButAlsoObserver_B oob = new NotOnlyObservableButAlsoObserver_B();
oob.addObserver(ooa);
ooa.addObserver(oob);
oob.setData(1);
}
}
五、观察者模式的应用场景
介绍完了观察者模式,回过头来再看看它的优缺点,再总结下它的使用场景。
优点:
观察者模式可以让主题对象和观察者对象之间松耦合,主题只知道观察者实现了某个接口,而无需知道观察者的具体类,以及具体如何实现。这样,即使之后要添加新的观察者类型,仍然只需实现该接口然后注册到主题对象中即可,无需修改已有代码,这也符合了对修改关闭,对扩展开发。
缺点:
1.首先观察者模式限定了类必须继承自Observer基类,单继承不友好。
2.观察者模式限定了成员函数的名字,参数,返回值等。
当一个对象改变需要改变其他对象,并且不知道有多少对象有待改变时,考虑使用观察者模式。
使用场景:
- 1.当一个抽象模型有两个方面,其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。
- 2.当对一个对象的改变需要同时改变其它对象,而不知道具体有多少对象有待改变。
- 3.当一个对象必须通知其它对象, 而它又不能假定其它对象是谁。
总之,当一个对象的改变需要同时改变其它对象,并且它不知道具体有多少对象有待改变的时候,应该考虑使用观察者模式。而使用观察者模式的动机在于:将一个系统分割成一系列相互协作的类有一个很不好的副作用,就是需要维护相关对象间的一致性,我们不希望为了维持一致性而使各类紧密耦合,这样会给维护、扩展和重用都带来不便,而观察者模式所做的工作就是在解除耦合。
为了更好的理解什么是观察者模式,下面举一些可能用到该模式的情形或例子:
- (1)周期性任务。比如linux中的周期性任务命令crontab命令,win7下的定时关机命令shutdown -s -t 1200(1200s后关机)。这些命令当预期系统时间到后,就可以通知相应的观察者激活相应的命令。
- (2)重新加载配置文件。现在做大型系统基本都会有配置文件,例如在SSH项目中每次修改配置文件后,都需要重新启动服务器才能使得新的配置文件生效(当然SSH中貌似已经提供了参数设置,当配置文件修改时,可以自动重新加载)。