23种常用设计模式之观察者模式

说明

观察者模式(又被称为发布-订阅(Publish/Subscribe)模式,属于行为型模式的一种,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态变化时,会通知所有的观察者对象,使他们能够自动更新自己

应用场景

  • 对一个对象状态的更新,需要其他对象同步更新,而且其他对象的数量动态可变。
  • 对象仅需要将自己的更新通知给其他对象而不需要知道其他对象的细节。

模式特征

Subject:抽象主题角色,他把所有观察者对象保存在一个集合里,可以有任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象。
ConcreteSubject:具体主题角色,该角色将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发送通知。
Observer:抽象观察者角色,是观察者者的抽象类,它定义了一个更新接口,使得在得到主题更改通知时更新自己。
ConcreteObserver:具体观察者角色,实现抽象观察者定义的更新接口,以便在得到主题更改通知时更新自身的状态。

代码实现

代码场景:马上要考试了,对学霸们来说顺利通过考试自然不是什么难事,但对学酥来说,就没那么容易了。为了顺利通过考试,学酥就找到了好友学霸,要求订(zhu)阅(gong)一下。我们通过观察者模式来实现这个场景

  • 抽象主题角色
public interface Subject {
    void register(Observer observer);
    void remove(Observer observer);
    void notifys();
}
  • 具体主题角色
public class SmartStudent implements Subject {

    private List<Observer> list = new ArrayList<>();

    private String name;

    private String answer;

    public SmartStudent(String name) {
        this.name = name;
    }

    @Override
    public void register(Observer observer) {
        list.add(observer);
    }

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

    @Override
    public void notifys() {
        for (Observer observer : list){
            observer.receive(answer);
        }
    }

    public void answerQuestion(String question){
        answer = getAnswer(question);
        notifys();
    }

    private String getAnswer(String question) {
        //省略做题细节
        System.out.println(name + "做出了第" + question + "题的答案");
        return question;
    }
}
  • 抽象观察者角色
public interface Observer {
    void receive(String answer);
}
  • 具体观察者角色
public class LazyStudent implements Observer {

    private String name;

    public LazyStudent(String name) {
        this.name = name;
    }

    @Override
    public void receive(String answer) {
        System.out.println(name + "收到了第" + answer + "题的答案");
    }
}
  • 客户端测试
public class Client {
    public static void main(String[] args) {
        SmartStudent xiaohong = new SmartStudent("小红");
        LazyStudent xiaoming = new LazyStudent("小明");
        LazyStudent xiaogang = new LazyStudent("小刚");
        xiaohong.register(xiaoming);
        xiaohong.register(xiaogang);
        System.out.println("考试开始...");
        for (int i = 0; i < 10; i++) {
            xiaohong.answerQuestion(i + 1 + "");
        }
        System.out.println("考试结束。");
    }
}
  • 结果
考试开始...
小红做出了第1题的答案
小明收到了第1题的答案
小刚收到了第1题的答案
小红做出了第2题的答案
小明收到了第2题的答案
小刚收到了第2题的答案
小红做出了第3题的答案
小明收到了第3题的答案
小刚收到了第3题的答案
小红做出了第4题的答案
小明收到了第4题的答案
小刚收到了第4题的答案
小红做出了第5题的答案
小明收到了第5题的答案
小刚收到了第5题的答案
小红做出了第6题的答案
小明收到了第6题的答案
小刚收到了第6题的答案
小红做出了第7题的答案
小明收到了第7题的答案
小刚收到了第7题的答案
小红做出了第8题的答案
小明收到了第8题的答案
小刚收到了第8题的答案
小红做出了第9题的答案
小明收到了第9题的答案
小刚收到了第9题的答案
小红做出了第10题的答案
小明收到了第10题的答案
小刚收到了第10题的答案
考试结束。

优缺点

优点
  • 观察者和被观察者是抽象耦合的。
  • 建立一套触发机制。
缺点
  • 如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
  • 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
  • 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。

Java内置观察者模式

特征
  • 提供了java.util.Observable类和java.util.Observer接口
  • 使用时具体主题角色需要继承Observable类
  • 具体观察者角色需要实现Observer接口
  • 通知时需要调用setChanged()函数变更状态
  • 通知调用notifyObservers(Object arg)或notifyObservers()函数
  • 如需再次通知,需清除状态变更clearChanged()
代码实现
  • 具体主题类
import java.util.Observable;

public class SmartStudent1 extends Observable {
    private String name;

    private String answer;

    public SmartStudent1(String name) {
        this.name = name;
    }

    public void answerQuestion(String question){
        answer = getAnswer(question);
        //设置状态变更
        setChanged();
        //调用父类方法触发通知
        notifyObservers(answer);
        //清除状态变更
        clearChanged();
    }

    private String getAnswer(String question) {
        //省略做题细节
        System.out.println(name + "做出了第" + question + "题的答案");
        return question;
    }
}
  • 具体观察者
public class LazyStudent1 implements Observer {

    private String name;

    public LazyStudent1(String name) {
        this.name = name;
    }

    @Override
    public void update(Observable o, Object arg) {
        System.out.println(name + "收到了第" + arg + "题的答案");
    }
}
  • 客户端测试
public class Client1 {
    public static void main(String[] args) {
        SmartStudent1 xiaohong = new SmartStudent1("小红");
        LazyStudent1 xiaoming = new LazyStudent1("小明");
        LazyStudent1 xiaogang = new LazyStudent1("小刚");
        xiaohong.addObserver(xiaoming);
        xiaohong.addObserver(xiaogang);
        System.out.println("考试开始...");
        for (int i = 0; i < 10; i++) {
            xiaohong.answerQuestion(i + 1 + "");
        }
        System.out.println("考试结束。");
    }
}
Java内置观察者模式总结
  • 具体主题角色需要继承Observable,有一定的局限性
  • 实现起来比自己实现稍微简单一些
原创文章 67 获赞 31 访问量 5万+

猜你喜欢

转载自blog.csdn.net/u012534326/article/details/102907022