Java描述设计模式(11):观察者模式
本文源碼:GitHub·點這里 || GitEE·點這里
一、觀察者模式
1、概念描述
觀察者模式是對象的行為模式,又叫發布-訂閱(Publish/Subscribe)模式。觀察者模式定義了一種一對多的依賴關系,讓多個觀察者對象同時監聽某一個主題對象,主題對象在狀態發生變化時,會通知所有觀察者對象。Redis和常用消息中間件的發布訂閱模式,都是基于該原理實現。
2、核心角色
- 抽象主題角色
抽象主題角色把所有對觀察者對象的統一聚集管理,每個主題都可以有一個或多個觀察者。抽象主題提供一個接口,可以增加和刪除觀察者對象,抽象主題角色又叫做抽象被觀察者(Observable)角色。
- 具體主題角色
將有關狀態存入具體觀察者對象;在具體主題的內部狀態改變時,給所有登記過的觀察者發出通知。具體主題角色又叫做具體被觀察者角色。
- 抽象觀察者角色
為所有的具體觀察者定義一個接口,在得到主題的通知時更新自己,這個接口叫做更新接口。
- 具體觀察者角色
具體觀察者角色實現抽象觀察者角色所要求的更新接口,以便使本身的狀態與主題的狀態協調同步。如果需要,具體觀察者角色可以保持一個指向具體主題對象的引用。
3、源代碼實現
- 基于消息推送模式
主題對象向觀察者推送主題的消息,不管觀察者是否需要。
/*** 觀察者設計模式*/ public class C01_Observer {public static void main(String[] args) {// 創建主題對象ConcreteSubject subject = new ConcreteSubject();// 創建觀察者對象Observer observer1 = new ConcreteObserver("觀察者A");Observer observer2 = new ConcreteObserver("觀察者B");// 注冊觀察者subject.attach(observer1);subject.attach(observer2);// 修改主題狀態subject.change("New State !");/*** 主題狀態:New State !*【觀察者A】狀態:New State !*【觀察者B】狀態:New State !*/} } // 抽象主題角色 abstract class Subject {// 保存注冊的觀察者對象private List<Observer> list = new ArrayList<>();/*** 注冊觀察者對象*/public void attach (Observer observer){list.add(observer);System.out.println("注冊一個觀察者:"+observer.getClass().getName());}/*** 刪除觀察者對象*/public void delete (Observer observer){list.remove(observer);System.out.println("刪除一個觀察者:"+observer);}/*** 通知所有注冊的觀察者對象*/public void notifyObserver (String newState){for (Observer observer : list) {observer.update(newState);}} } // 具體主題角色 class ConcreteSubject extends Subject{private String state ;public String getState (){return state ;}public void change (String newState){state = newState;System.out.println("主題狀態:"+state);//狀態發生改變,通知各個觀察者this.notifyObserver(state);} } // 抽象觀察者角色 interface Observer {/*** 更新接口*/void update (String state); } // 具體觀察者角色 class ConcreteObserver implements Observer{private String name ;// 觀察者狀態private String observerState ;public ConcreteObserver (String name){this.name = name ;}/*** 更新觀察者的狀態,使其與目標的狀態保持一致*/@Overridepublic void update(String state) {observerState = state ;System.out.println("【"+this.name+"】狀態:"+observerState);} }- 基于消息拉取模式
主題對象在通知觀察者的時候,傳遞少量信息。如果觀察者需要該消息內容,由觀察者主動到主題對象中獲取,相當于是觀察者從主題對象中拉數據。
該案例基于上述案例修改,觀察者獲取主題對象的消息話題,只有自己感興趣的話題,才進一步獲取內容。
public class C02_Observer_Pull {public static void main(String[] args) {// 創建主題對象ConcreteSubject1 subject = new ConcreteSubject1();// 創建觀察者對象Observer1 observer1 = new ConcreteObserver1("觀察者A","JAVA");Observer1 observer2 = new ConcreteObserver1("觀察者B","MySQL");// 注冊觀察者subject.attach(observer1);subject.attach(observer2);/** 修改主題狀態* 主題狀態:JAVA State !* 【觀察者A】狀態:JAVA State !* 主題狀態:MySQL State !* 【觀察者B】狀態:MySQL State !*/subject.change("JAVA State !","JAVA");subject.change("MySQL State !","MySQL");} } abstract class Subject1 {// 保存注冊的觀察者對象private List<Observer1> list = new ArrayList<>();/*** 注冊觀察者對象*/public void attach (Observer1 observer){list.add(observer);}/*** 刪除觀察者對象*/public void delete (Observer1 observer){list.remove(observer);System.out.println("刪除一個觀察者:"+observer);}/*** 通知所有注冊的觀察者對象,傳入消息的話題*/public void notifyObservers (String msgTopic){for (Observer1 observer : list){observer.update(this);}} } class ConcreteSubject1 extends Subject1 {private String state ;private String msgTopic ;public String getState (){return state ;}public String getMsgTopic (){return msgTopic ;}public void change (String newState,String newMsgTopic){this.state = newState ;this.msgTopic = newMsgTopic ;System.out.println("主題狀態:"+state);this.notifyObservers(msgTopic);} }interface Observer1 {/*** 更新接口* @param subject 傳入主題對象,方面獲取相應的主題對象的狀態*/void update(Subject1 subject); } class ConcreteObserver1 implements Observer1{private String name ;// 選擇話題private String msgTopic ;// 觀察者狀態private String observerState ;public ConcreteObserver1 (String name,String msgTopic){this.name = name ;this.msgTopic = msgTopic ;}@Overridepublic void update(Subject1 subject) {ConcreteSubject1 concreteSubject1 = (ConcreteSubject1)subject ;// 只有指定話題才拉取消息if (concreteSubject1.getMsgTopic().equals(msgTopic)){observerState = concreteSubject1.getState();System.out.println("【"+this.name+"】狀態:"+observerState);}} }4、兩種模式比較
推模式是假定主題對象知道觀察者需要的數據,直接推送,使得觀察者對象難以復用;而拉模式是主題對象不知道觀察者具體需要什么數據,將把自身傳遞給觀察者,按需要取值。
二、JDK中應用
JAVA語言的java.utill類庫里面,提供了一個Observable類以及一個Observer接口,構成JAVA語言對觀察者模式的支持。
1、Observer接口
這個接口只定義了一個方法,即update()方法,當被觀察者對象的狀態發生變化時,被觀察者對象的notifyObservers()方法就會調用這一方法。
package java.util; /*** A class can implement the <code>Observer</code> interface when it* wants to be informed of changes in observable objects. */ public interface Observer {/*** This method is called whenever the observed object is changed. An* application calls an <tt>Observable</tt> object's*/void update(Observable o, Object arg); }2、Observable類
被觀察者類都是java.util.Observable類的子類。java.util.Observable提供方法支持觀察者對象。
- setChanged方法:觀察者對象的狀態發生了變化。
- notifyObservers:調用所有登記過的觀察者對象的update()方法。
3、應用案例
public class C03_Observer_JDK {public static void main(String[] args) {//創建被觀察者對象MsgSource msgSource = new MsgSource();//創建觀察者對象,并將被觀察者對象登記MsgConsumer watcher = new MsgConsumer(msgSource);msgSource.setData("Hello,Java");msgSource.setData("Bye Java");} } class MsgSource extends Observable {private String data = "";public String getData() {return data;}public void setData(String data) {if(!this.data.equals(data)){this.data = data;setChanged();}notifyObservers();} } class MsgConsumer implements java.util.Observer {// 添加觀察者public MsgConsumer(Observable msgSource){msgSource.addObserver(this);}// 狀態獲取@Overridepublic void update(Observable o, Object arg) {System.out.println("消息內容:" + ((MsgSource)o).getData());} }三、優缺點總結
觀察者模式的主要的作用是對象解耦,將觀察者和被觀察者隔離。
程序中包括多個被觀察者和多個被觀察者,開發和調試比較復雜,而且Java中的消息的通知默認是順序執行的,一個觀察者的執行阻塞會影響整體的執行效率。
四、源代碼地址
GitHub·地址 https://github.com/cicadasmile/model-arithmetic-parent GitEE·地址 https://gitee.com/cicadasmile/model-arithmetic-parent總結
以上是生活随笔為你收集整理的Java描述设计模式(11):观察者模式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 蓝桥杯-算法训练 2的次幂表示
- 下一篇: FireBug 调试JS入门 —如何调试