Define a one-to-many dependency between objects where a state change in one object results in all its dependents being notified and updated automatically.
成都創(chuàng)新互聯(lián)公司自2013年起,先為臨沂等服務建站,臨沂等地企業(yè),進行企業(yè)商務咨詢服務。為臨沂企業(yè)網(wǎng)站制作PC+手機+微官網(wǎng)三網(wǎng)同步一站式服務解決您的所有建站問題。
直譯過來就是,定義對象間的一對多依賴關系,當一個對象的狀態(tài)變更會自動通知和更新所有依賴項。像發(fā)布/訂閱模式,事件通知模式,數(shù)據(jù)源/監(jiān)聽模式等都是性質(zhì)一樣。
類圖:Subject類不會直接更新從屬對象的狀態(tài)。相反,Subject引用了用于更新狀態(tài)的觀察者接口(update()),這使得Subject獨立于依賴對象的狀態(tài)更新方式。Observer1和Observer2類通過將狀態(tài)與Subject的狀態(tài)同步來實現(xiàn)Observer接口。
時序圖:Observer1和Observer2對象調(diào)用Subject1上的attach(this)來注冊自己。假如Subject1的狀態(tài)發(fā)生變更,Subject1本身調(diào)用notify()。notify()對已注冊的Observer1和Observer2對象調(diào)用update(),后者從Subject1請求已更改的數(shù)據(jù)(getState())以更新(同步)其狀態(tài)。
cdn.xitu.io/2019/4/8/169fa85625382e27?w=600&h=240&f=jpeg&s=26680">
Subject(目標):目標又稱為主題,它是指被觀察的對象。在目標中定義了一個觀察者集合,一個觀察目標可以接受任意數(shù)量的觀察者來觀察,它提供一系列方法來增加和刪除觀察者對象,同時它定義了通知方法notify()。目標類可以是接口,也可以是抽象類或具體類。
ConcreteSubject(具體目標):具體目標是目標類的子類,通常它包含有經(jīng)常發(fā)生改變的數(shù)據(jù),當它的狀態(tài)發(fā)生改變時,向它的各個觀察者發(fā)出通知;同時它還實現(xiàn)了在目標類中定義的抽象業(yè)務邏輯方法(如果有的話)。如果無須擴展目標類,則具體目標類可以省略。
Observer(觀察者):觀察者將對觀察目標的改變做出反應,觀察者一般定義為接口,該接口聲明了更新數(shù)據(jù)的方法update(),因此又稱為抽象觀察者。
ConcreteObserver(具體觀察者):在具體觀察者中維護一個指向具體目標對象的引用,它存儲具體觀察者的有關狀態(tài),這些狀態(tài)需要和具體目標的狀態(tài)保持一致;它實現(xiàn)了在抽象觀察者Observer中定義的update()方法。
觀察者模式描述了如何建立對象與對象之間的依賴關系,以及如何構(gòu)造滿足這種需求的系統(tǒng)。觀察者模式包含觀察目標和觀察者兩類對象,一個目標可以有任意數(shù)目的與之相依賴的觀察者,一旦觀察目標的狀態(tài)發(fā)生改變,所有的觀察者都將得到通知。作為對這個通知的響應,每個觀察者都將監(jiān)視觀察目標的狀態(tài)以使其狀態(tài)與目標狀態(tài)同步,這種交互也稱為發(fā)布-訂閱(Publish-Subscribe)。觀察目標是通知的發(fā)布者,它發(fā)出通知時并不需要知道誰是它的觀察者,可以有任意數(shù)目的觀察者訂閱它并接收通知。
源碼地址
博客訂閱的功能,抽象主題中維護訂閱關系,同時引入普通和vip觀察者。
//抽象主題
@Data
public abstract class Subject {
//主題訂閱者們
private List observerList = Lists.newArrayList();
//訂閱
public void register(Observer observer) {
observerList.add(observer);
}
//取消訂閱
public void remove(Observer observer) {
observerList.remove(observer);
}
//發(fā)布東西
public abstract void publish(String msg);
}
//抽象觀察者
@Slf4j
@Data
@AllArgsConstructor
public abstract class Observer {
//觀察者名稱
private String name;
//更新狀態(tài),由主題調(diào)度
public void update(Object subject, Object args) {
log.info("{}獲取到變更通知:{}", name, args);
}
}
//博客主題
@Slf4j
public class Blog extends Subject {
@Override
public void publish(String msg) {
log.info("發(fā)布msg:{}", msg);
//通知訂閱者
getObserverList().forEach(observer -> observer.update(this, msg));
}
}
//普通用戶觀察者
@Slf4j
public class NormalObserver extends Observer {
public NormalObserver(String name) {
super(name);
}
@Override
public void update(Object subject, Object args) {
super.update(subject, args);
log.info("{}獲取到變更通知:普通用戶可以不緩存", getName());
}
}
//vip用戶觀察者
@Slf4j
public class VipObserver extends Observer {
public VipObserver(String name) {
super(name);
}
@Override
public void update(Object subject, Object args) {
super.update(subject, args);
log.info("{}獲取到變更通知:vip可以緩存", getName());
}
}
@Slf4j
public class Application {
public static void main(String[] args) {
Blog blog = new Blog();
VipObserver wang = new VipObserver("老王99");
VipObserver lee = new VipObserver("老李");
NormalObserver four = new NormalObserver("小四");
NormalObserver twoEgg = new NormalObserver("二蛋");
log.info("---------------------begin--------------------");
// 用戶訂閱博客,普通和vip用戶
new Thread(() -> {
blog.register(wang);
sleep(2);
blog.register(lee);
sleep(2);
blog.register(four);
}).start();
// 博客線程每隔2秒發(fā)布一次文章, 總共發(fā)布4次
new Thread(() -> {
IntStream.rangeClosed(1, 4).forEach(i -> {
blog.publish(String.format("新把戲第%s次", i));
sleep(2);
});
}).start();
// 有用戶退出訂閱博客,也有二蛋加入訂閱
new Thread(() -> {
sleep(3);
blog.remove(lee);
sleep(2);
blog.register(twoEgg);
}).start();
}
private static void sleep(int seconds) {
try {
TimeUnit.SECONDS.sleep(seconds);
} catch (InterruptedException e) {
log.error("error : ", e);
}
}
}
jdk內(nèi)置的Obverser和Observable
@Slf4j
public class ObserverApplication {
public static void main(String[] args) {
log.info("Enter Text: ");
EventSource eventSource = new EventSource();
eventSource.addObserver((obj, arg) -> {
log.info("Received response: {}", arg);
});
eventSource.addObserver((obj, arg) -> {
log.info("Received response2: {},,{}", arg, obj
);
});
new Thread(eventSource).start();
}
}
class EventSource extends Observable implements Runnable {
public void run() {
while (true) {
String response = new Scanner(System.in).next();
setChanged();
notifyObservers(response);
}
}
}
它使得一個對象可以靈活的將消息發(fā)送給感興趣的對象
java.util.EventListener
javax.servlet.http.HttpSessionBindingListener
javax.servlet.http.HttpSessionAttributeListener
javax.faces.event.PhaseListener
Listener從名字上看就明白是監(jiān)聽的意思了。
jdk中內(nèi)置的觀察者模式。即是java.util.Observer(接口)和java.util.Observable(類)。
簡單的說說這個。觀察者接口(java.util.Observer),主題(java.util.Observable)。實現(xiàn)觀察者接口和繼承主題。通過抽象主題的addObserver()注冊觀察者,deleteObserver()移除觀察者。
首先通過調(diào)用主題類的setChange()告知狀態(tài)變更,隨后調(diào)用notifyObservers方法(可傳可不傳參數(shù))去通知觀察者,最后因為主題在notifyObservers時會主動調(diào)用觀察者的update()方法,改方法有2個參數(shù),第一個為主題對象,第二個為可變參數(shù)。
觀察者模式是一種使用頻率非常高的設計模式,JDK中就帶有實現(xiàn)。無論是移動應用、Web應用或者桌面應用,觀察者模式幾乎無處不在,它為實現(xiàn)對象之間的聯(lián)動提供了一套完整的解決方案,凡是涉及到一對一或者一對多的對象交互場景都可以使用觀察者模式。觀察者模式廣泛應用于各種編程語言的GUI事件處理的實現(xiàn),在基于事件的XML解析技術(如SAX2)以及Web事件處理中也都使用了觀察者模式。
(1) 觀察者模式可以實現(xiàn)表示層和數(shù)據(jù)邏輯層的分離,定義了穩(wěn)定的消息更新傳遞機制,并抽象了更新接口,使得可以有各種各樣不同的表示層充當具體觀察者角色。
(2) 觀察者模式在觀察目標和觀察者之間建立一個抽象的耦合。觀察目標只需要維持一個抽象觀察者的集合,無須了解其具體觀察者。由于觀察目標和觀察者沒有緊密地耦合在一起,因此它們可以屬于不同的抽象化層次。
(3) 觀察者模式支持廣播通信,觀察目標會向所有已注冊的觀察者對象發(fā)送通知,簡化了一對多系統(tǒng)設計的難度。
(4) 觀察者模式滿足“開閉原則”的要求,增加新的具體觀察者無須修改原有系統(tǒng)代碼,在具體觀察者與觀察目標之間不存在關聯(lián)關系的情況下,增加新的觀察目標也很方便。
(1) 如果一個觀察目標對象有很多直接和間接觀察者,將所有的觀察者都通知到會花費很多時間。
(2) 如果在觀察者和觀察目標之間存在循環(huán)依賴,觀察目標會觸發(fā)它們之間進行循環(huán)調(diào)用,可能導致系統(tǒng)崩潰。
(3) 觀察者模式?jīng)]有相應的機制讓觀察者知道所觀察的目標對象是怎么發(fā)生變化的,而僅僅只是知道觀察目標發(fā)生了變化。
1、JAVA 中已經(jīng)有了對觀察者模式的支持類。 2、避免循環(huán)引用。 3、如果順序執(zhí)行,某一觀察者錯誤會導致系統(tǒng)卡殼,一般采用異步方式。
Observer pattern
觀察者模式|菜鳥教程
細數(shù)JDK里的設計模式
設計模式總結(jié)(Java)—— 觀察者模式
歡迎關注