定义 定义了对象之间的一对多依赖,让多个观察者对象同时监听某一个主题对象,当主题对象发生变化时,它的所有依赖者(观察者)都会收到通知并更新。
类型 行为型
适用场景 1 关联行为场景,建立一套触发机制。如:注某个产品的价格,然后进行通知,其中价格的变动可能会影响一条链,就像是一个触发链条。这样就可以使用观察者模式创建一个种链式发机制。
优点 1 2 3 4 1. 观察者和被观察者之间建立一个抽象的耦合 - 因为是抽象的耦合关系,不管是增加观察者还是被观察者都很容易扩展 2. 支持广播通信 - 类似消息广播,需要监听主题的只需要注册就可以了
缺点 1 2 3 4 5 1. 观察者之间有过多的细节依赖、提高了时间消耗及程序复杂度 - 过多的依赖:触发机制和触发链条 - 提高了时间消耗及程序复杂度:如果一个被观察对象有多个直接或间接观察者,一旦被观察者变化,然后发出通知,将所有观察者都通知到会花费一些时间 2. 使用要得当,避免循环调用 - 如果在观察者和被观察者之间有循环依赖的话,被观察者(主题对象)会触发它们之间进行循环调用,这样会导致系统崩溃
简单需求 学生在学习课程的时候可能会提出问题,而老师则是关注自己的课程,有学生提出自己课程的问题就给出解答
观察者模式演练
被观察者需要继承的类
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 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 package java.util;public class Observable { private boolean changed = false ; private Vector<Observer> obs; public Observable () { obs = new Vector<>(); } public synchronized void addObserver (Observer o) { if (o == null ) throw new NullPointerException(); if (!obs.contains(o)) { obs.addElement(o); } } public synchronized void deleteObserver (Observer o) { obs.removeElement(o); } public void notifyObservers () { notifyObservers(null ); } public void notifyObservers (Object arg) { Object[] arrLocal; synchronized (this ) { if (!changed) return ; arrLocal = obs.toArray(); clearChanged(); } for (int i = arrLocal.length-1 ; i>=0 ; i--) ((Observer)arrLocal[i]).update(this , arg); } public synchronized void deleteObservers () { obs.removeAllElements(); } protected synchronized void setChanged () { changed = true ; } protected synchronized void clearChanged () { changed = false ; } public synchronized boolean hasChanged () { return changed; } public synchronized int countObservers () { return obs.size(); } }
观察者需要实现的接口
1 2 3 4 5 6 7 8 9 10 11 12 package java.util;public interface Observer { void update (Observable o, Object arg) ; }
主题类(被观察者)
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 48 49 50 51 52 53 54 55 56 57 58 59 package com.design.pattern.observer;import lombok.Data;import lombok.extern.slf4j.Slf4j;import java.util.ArrayList;import java.util.List;import java.util.Observable;@Data @Slf 4jpublic class Course extends Observable { private String name; private List<Question> questions = new ArrayList<>(); public Course (String name) { this .name = name; } public void addQuestion (Question question) { questions.add(question); } public void produceQuestion (Course course) { questions.stream().forEach(question -> { log.info(String.format("%s在%s提出了问题" , question.getUserName(), course.getName())); setChanged(); notifyObservers(question); } ); } }
观察者
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 package com.design.pattern.observer;import lombok.AllArgsConstructor;import lombok.Data;import lombok.extern.slf4j.Slf4j;import java.util.Observable;import java.util.Observer;@Data @AllArgsConstructor @Slf 4jpublic class Teacher implements Observer { private String name; @Override public void update (Observable o, Object arg) { Course course = (Course) o; Question question = (Question) arg; log.info(String.format("%s课程被%s同学提出%s的问题,需要%s解答" ,course.getName(),question.getUserName(),question.getQuestionContent(),name)); } }
应用辅助类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package com.design.pattern.observer;import lombok.Builder;import lombok.Data;@Data @Builder public class Question { private String userName; private String questionContent; }
应用层
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 package com.design.pattern.observer;import org.junit.Test;public class Client { @Test public void test () { Course course = new Course("《Java从入门到放弃》" ); Teacher teacher = new Teacher("Java学院老师" ); Teacher teacher1 = new Teacher("鼓励师" ); course.addObserver(teacher); course.addObserver(teacher1); course.addQuestion(Question.builder() .userName("gentryhuang" ) .questionContent("Java学不完,需要放弃吗?" ) .build()); course.addQuestion(Question.builder() .userName("xw" ) .questionContent("快看,又一个学Java的转行了,要跑路吗?" ) .build()); course.produceQuestion(course); } }
观察者模式源码解析 监听器实现方案就是观察者模式实现的一种
Guava中观察者模式的使用
使用@Subscribe进行方法标注
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package com.design.pattern.observer.guava;import com.google.common.eventbus.Subscribe;import lombok.extern.slf4j.Slf4j;@Slf 4jpublic class GuavaEvent { @Subscribe public void subscribe (String event) { log.info("执行subscribe方法,传入参数是:" + event); } }
在应用层把订阅者进行注册
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 package com.design.pattern.observer.guava;import com.google.common.eventbus.EventBus;import org.junit.Test;public class GuavaEventTest { @Test public void test () { EventBus eventBus = new EventBus(); GuavaEvent guavaEvent = new GuavaEvent(); eventBus.register(guavaEvent); eventBus.post("post的内容" ); } }