您好,登錄后才能下訂單哦!
本文源碼:GitHub·點這里 || GitEE·點這里
觀察者模式是對象的行為模式,又叫發布-訂閱(Publish/Subscribe)模式。觀察者模式定義了一種一對多的依賴關系,讓多個觀察者對象同時監聽某一個主題對象,主題對象在狀態發生變化時,會通知所有觀察者對象。Redis和常用消息中間件的發布訂閱模式,都是基于該原理實現。
抽象主題角色把所有對觀察者對象的統一聚集管理,每個主題都可以有一個或多個觀察者。抽象主題提供一個接口,可以增加和刪除觀察者對象,抽象主題角色又叫做抽象被觀察者(Observable)角色。
將有關狀態存入具體觀察者對象;在具體主題的內部狀態改變時,給所有登記過的觀察者發出通知。具體主題角色又叫做具體被觀察者角色。
為所有的具體觀察者定義一個接口,在得到主題的通知時更新自己,這個接口叫做更新接口。
具體觀察者角色實現抽象觀察者角色所要求的更新接口,以便使本身的狀態與主題的狀態協調同步。如果需要,具體觀察者角色可以保持一個指向具體主題對象的引用。
主題對象向觀察者推送主題的消息,不管觀察者是否需要。
/**
* 觀察者設計模式
*/
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 ;
}
/**
* 更新觀察者的狀態,使其與目標的狀態保持一致
*/
@Override
public 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 ;
}
@Override
public void update(Subject1 subject) {
ConcreteSubject1 concreteSubject1 = (ConcreteSubject1)subject ;
// 只有指定話題才拉取消息
if (concreteSubject1.getMsgTopic().equals(msgTopic)){
observerState = concreteSubject1.getState();
System.out.println("【"+this.name+"】狀態:"+observerState);
}
}
}
推模式是假定主題對象知道觀察者需要的數據,直接推送,使得觀察者對象難以復用;而拉模式是主題對象不知道觀察者具體需要什么數據,將把自身傳遞給觀察者,按需要取值。
JAVA語言的java.utill類庫里面,提供了一個Observable類以及一個Observer接口,構成JAVA語言對觀察者模式的支持。
這個接口只定義了一個方法,即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);
}
被觀察者類都是java.util.Observable類的子類。java.util.Observable提供方法支持觀察者對象。
package java.util;
public class Observable {
private boolean changed = false;
private Vector obs;
/** Construct an Observable with zero Observers. */
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);
}
/**
* 如果本對象有變化(那時hasChanged 方法會返回true)
* 調用本方法通知所有登記的觀察者,即調用它們的update()方法
* 傳入this和arg作為參數
*/
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();
}
/** 將“已變化”設置為true */
protected synchronized void setChanged() {
changed = true;
}
/** 將“已變化”重置為false */
protected synchronized void clearChanged() {
changed = false;
}
public synchronized boolean hasChanged() {
return changed;
}
public synchronized int countObservers() {
return obs.size();
}
}
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);
}
// 狀態獲取
@Override
public 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
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。