关注「Java视界」公众号,获取更多技术干货

观察者模式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已经为我们提供了观察者模式相关的类及方法。

分别是:

  1. java.util.Observable ——被观察者,程序中的被观察者类,需要继承这个类。
  2. 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中貌似已经提供了参数设置,当配置文件修改时,可以自动重新加载)。

 

posted @ 2022-06-25 14:02  沙滩de流沙  阅读(104)  评论(0编辑  收藏  举报

关注「Java视界」公众号,获取更多技术干货