架構師內功心法,先發布後訂閱的觀察者模式詳解

一、觀察者模式的應用場景

生活中的微信朋友圈動態通知、工作中的郵件通知、學校操場上的廣播通知、電腦桌面應用程序的事件響應等等,這些都是觀察者模式的現實生活場景。觀察者模式(Observer Pattern)定義了對象之間一對多的依賴,讓多個觀察者監聽一個主體對象,當主體對象發生變化時,它的所有觀察者都會收到通知並且更新。

觀察者模式也叫發佈訂閱模式。

架構師內功心法,先發布後訂閱的觀察者模式詳解

1.1 學生在線提問的案例

現在還是新冠狀病毒疫情期間,很多學生在家裡學習遇到問題,難免需要請教自己的老師,當學生在某教育平臺上提問的時候,如果有設置指定的老師進行答覆,那麼對應的老師會受到相應的郵件通知,這就是觀察者模式的一種應用場景。拿到這個需要的時候我們可能會想到異步隊列、消息中間件MQ等一些技術手段,其實JDK本身就提供了這樣的API,那麼我們來寫關於這個場景的示例代碼,創建 Question 問題類:

<code>public class Question {

private String userName;
private String content;

public String getUserName() {
return userName;
}

public void setUserName(String userName) {
this.userName = userName;
}

public String getContent() {
return content;
}

public void setContent(String content) {
this.content = content;
}
}
/<code>

再創建教育平臺被觀察者 Education 類繼承 Observable :

<code>public class Education extends Observable {

private final String EDU_NAME = "xxx互聯網教育平臺";

private Education() {}

private static Education education = null;

public static Education getInstance() {
if(null == education) {
education = new Education();
}
return education;
}

public void publishQuestion(Question question) {
System.out.println(question.getUserName() + "在" + EDU_NAME + "上提交了一個問題!");
this.setChanged();
this.notifyObservers(question);
}

}
/<code>

創建老師 Teacher 類實現 Observer接口:

<code>public class Teacher implements Observer {

private String name;

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

@Override
public void update(Observable o, Object arg) {
Education education = (Education) o;
Question question = (Question) arg;
System.out.println("===============================");
System.out.println(name + "老師,你好!\\n" +
"您收到了一個來自“" + education.getEduName() + "”的提問,希望您解答,問題內容如下:\\n" +
question.getContent() + "\\n" +
"提問者:" + question.getUserName());
}

}
/<code>

測試main方法:

<code> public static void main(String[] args) {

Education edu = Education.getInstance();
Teacher teacher1 = new Teacher("Kevin");
Teacher teacher2 = new Teacher("Jhon");

edu.addObserver(teacher1);
edu.addObserver(teacher2);

Question question = new Question();
question.setUserName("小芳");
question.setContent("觀察者模式有哪些應用場景?");

edu.publishQuestion(question);

}
/<code>

測試運行結果:

架構師內功心法,先發布後訂閱的觀察者模式詳解

二、觀察者模式在源碼中的體現

2.1 Spring中的ContextLoaderListener

Spring中的ContextLoaderListener實現了ServletContextListener接口,ServletContextListener又繼承了EventListener,我們來看下ContextLoaderListener源碼:

<code>public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
public ContextLoaderListener() {
}

public ContextLoaderListener(WebApplicationContext context) {
super(context);
}

public void contextInitialized(ServletContextEvent event) {
this.initWebApplicationContext(event.getServletContext());
}

public void contextDestroyed(ServletContextEvent event) {
this.closeWebApplicationContext(event.getServletContext());
ContextCleanupListener.cleanupAttributes(event.getServletContext());
}
}
/<code>

ServletContextListener :

<code>public interface ServletContextListener extends EventListener {
default void contextInitialized(ServletContextEvent sce) {
}

default void contextDestroyed(ServletContextEvent sce) {
}
}
/<code>

EventListener :

<code>/** 

* A tagging interface that all event listener interfaces must extend.
* @since JDK1.1
*/
public interface EventListener {
}
/<code>

2.2 基於Guava API實現觀察者模式

pom依賴

<code><dependency>
\t<groupid>com.google.guava/<groupid>
\t<artifactid>guava/<artifactid>
\t<version>23.0/<version>
/<dependency>
/<code>

創建監聽事件GuavaEvent:

<code>public class GuavaEvent {

@Subscribe
public void subscrible(String str) {
System.out.println("執行subscrible方法,傳入的參數是:" + str);
}

}
/<code>

測試main方法:

<code>public static void main(String[] args) {

EventBus eventBus = new EventBus();
GuavaEvent guavaEvent = new GuavaEvent();
eventBus.register(guavaEvent);
eventBus.post("Kevin");

}
/<code>

三、觀察者模式的優缺點

優點:

  • 觀察者和被觀察者之間建立了一個抽象的耦合;
  • 觀察者模式支持廣播通信。

缺點:

  • 觀察者之間有過多的細節依賴、提高時間消耗及程序的複雜度;
  • 使用要得當,要避免循環調用。


分享到:


相關文章: