概念
觀察者模式

image.png
發(fā)布/訂閱模式(比生產(chǎn)消費(fèi)更抽象)參考
基于典型觀察者模式實(shí)現(xiàn)的生產(chǎn)者和消費(fèi)者弊端:觀察者模式中觀察者需要直接訂閱目標(biāo)事件(直接引用目標(biāo)狀態(tài)),改變后直接接收響應(yīng),由于函數(shù)調(diào)用是同步的(阻塞),在消費(fèi)者的方法沒有返回之前,生產(chǎn)者只好一直等在那邊,阻塞進(jìn)程。 生產(chǎn)者和消費(fèi)者可以是兩個(gè)獨(dú)立的并發(fā)主體。生產(chǎn)者把制造出來的數(shù)據(jù)往緩沖區(qū)一丟,就可以再去生產(chǎn)下一個(gè)數(shù)據(jù),并不用關(guān)心消費(fèi)者狀態(tài); 發(fā)布訂閱模式就類似的多了一個(gè)發(fā)布通道(調(diào)度中心),一方面從生產(chǎn)者接收事件一方面向訂閱者發(fā)布事件。
image.png
1. 觀察者模式的模板
設(shè)計(jì)階段
image.png
//目標(biāo)對(duì)象,它持有觀察者抽象的集合
public class Subject {
//定義抽象目標(biāo)的狀態(tài)
private String subjectState;
//用來保存注冊(cè)的觀察者集合
private List<Observer_> observerList = new ArrayList<Observer_>();
//當(dāng)狀態(tài)改變時(shí)去通知
public void setSubjectState(String subjectState) {
this.subjectState = subjectState;
this.notifyObservers(); //通知
}
//添加觀察者
public void attach(Observer_ observer){
observerList.add(observer);
}
//刪除觀察者
public void detach(Observer_ observer){
observerList.remove(observer);
}
//通知所有的觀察者
public void notifyObservers(){
for (Observer_ observer: observerList){
observer.updata(this);
}
}
}
//觀察者
public class Observer_ {
//定義抽象的狀態(tài)
private String observerState;
//獲取目標(biāo)類的狀態(tài)同步到觀察者狀態(tài)中
public void updata(Subject subject){
observerState = subject.getSubjectState();
System.out.println("通知:" + observerState);
}
}
public class Client {
@Test
public void testObserver(){
//1 創(chuàng)建目標(biāo)
Subject subject = new Subject();
//2 創(chuàng)建觀察者
Observer_ observer1 = new Observer_();
observer1.setObserverState("observer1");
//注冊(cè)
subject.attach(observer1);
//通知
subject.setSubjectState("這是觀察者模式");
}
}

image.png
以上就是典型的觀察者模式的模板
2. 區(qū)別對(duì)待觀察者場(chǎng)景的問題
需求:最近經(jīng)常加班,身為項(xiàng)目經(jīng)理的你決定給大家買宵夜,張三只要雞翅,李四想要雞翅或者漢堡。這里宵夜種類就是目標(biāo),張三李四就是觀察者,如果通過以上代碼,是所有注冊(cè)都會(huì)被通知,這顯然是不合理的。代碼修改如下:
觀察者的接口
//觀察者接口定義一個(gè)更新接口的方法,目標(biāo)發(fā)生改變時(shí)通知觀察者進(jìn)行調(diào)用
public interface Observer {
//更新的接口
void update(SupperObject objecr);
//設(shè)置和取得觀察者名稱
void setObserverName(String observerName);
String getObserverName();
}
抽象的目標(biāo)類
public abstract class SupperObject {
//持有的觀察者集合
public List<Observer> observers = new ArrayList<Observer>();
//添加 刪除 觀察者
public void attach(Observer observer){
observers.add(observer);
}
public void detach(Observer observer){
observers.remove(observer);
}
//具體的提醒在子類邏輯中完成
protected abstract void notifyObservers();
}
//具體的目標(biāo)實(shí)現(xiàn)類
public class ConcreteSupperSubject extends SupperObject {
//定義宵夜的變量,雞翅,漢堡, 就是根據(jù)這個(gè)變量進(jìn)項(xiàng)更新(目標(biāo)狀態(tài))
private String supperContent;
@Override
protected void notifyObservers() {
//循環(huán)所有注冊(cè)的觀察者
for (Observer observer:observers){
//定義業(yè)務(wù)邏輯
//1 .如果雞翅就通知張三和李四,2.如果漢堡就只通知李四,3.其他都不通知
if ("雞翅".equals(this.getSupperContent())){
if ("張三".equals(observer.getObserverName())){
observer.update(this);
}
if ("李四".equals(observer.getObserverName())){
observer.update(this);
}
}
if ("漢堡".equals(this.supperContent)){
if ("李四".equals(observer.getObserverName())){
observer.update(this);
}
}
}
}
public String getSupperContent() {
return supperContent;
}
public void setSupperContent(String supperContent) {
this.supperContent = supperContent;
this.notifyObservers(); //通知 區(qū)別對(duì)待觀察者
}
}
//觀察者的具體實(shí)現(xiàn)
public class ConcreteObserver implements Observer{
//定義觀察者名稱 ,宵夜情況,提醒內(nèi)容
private String observerName;
private String supperContent; //目標(biāo)處獲取的狀態(tài)
private String remindThing;
@Override
public void update(SupperObject object) {
//獲取目標(biāo)的狀態(tài)
supperContent = ((ConcreteSupperSubject)object).getSupperContent();
System.out.println(observerName+"收到了"+supperContent+remindThing);
}
}
@Test
public void testSupperClient(){
//創(chuàng)建目標(biāo)
ConcreteSupperSubject supperSubject = new ConcreteSupperSubject();
//創(chuàng)建觀察者
ConcreteObserver zhangsan = new ConcreteObserver();
zhangsan.setObserverName("張三");
zhangsan.setRemindThing("雞翅真好吃!");
ConcreteObserver lisi = new ConcreteObserver();
lisi.setObserverName("李四");
lisi.setRemindThing("夜宵不錯(cuò),吃飽了可以好好干活了");
//注冊(cè)
supperSubject.attach(zhangsan);
supperSubject.attach(lisi);
//發(fā)布 雞翅,漢堡
supperSubject.setSupperContent("漢堡");
}

image.png

image.png
觀察者模式總結(jié)
- 存在目標(biāo)對(duì)象和觀察者兩個(gè)概念,做到解耦,但通知卻依賴了抽象的觀察者,假如觀察者無法抽象就無法通知更新。
- 所有的觀察者的動(dòng)作都一樣。如果不一樣就不能實(shí)現(xiàn)
事件機(jī)制
事件機(jī)制 就可以解決以上的問題,不需要觀察者的抽象。通過相應(yīng)的listener代替觀察者,類似觀察者模式卻解耦目標(biāo)和觀察。一般把事件對(duì)象作為業(yè)務(wù)接口的參數(shù),再根據(jù)相應(yīng)的條件觸發(fā)
事件機(jī)制一般需要3個(gè)角色,事件觸發(fā)源(source)、事件狀態(tài)對(duì)象(event)、處理邏輯(listener)
在spirng 中ServletContextListener接口通過web中的listener就可以在web啟動(dòng)時(shí)初始化spirng也是事件的一種應(yīng)用。
- 定義事件類
//定義的事件狀態(tài)類 也可以繼承java util 中的event
public class MyEvent {
//事件對(duì)象
private Object obj;
//狀態(tài)即觸發(fā)條件
private String state;
/**
* Constructs a prototypical MyEvent.
*
* @param source The object on which the MyEvent initially occurred.
* @throws IllegalArgumentException if source is null.
*/
public MyEvent(Object source, String state) {
//super(source);
this.obj = source;
this.state = state;
}
}
- 定義事件源handler
public class EventSource {
//保存監(jiān)聽器的列表,類似觀察者模式中保存所有觀察者的集合;子類可以保存自己的監(jiān)聽器。
private Collection listenets;
//注冊(cè)監(jiān)聽器
public void addEventListener(EventListener e){
if (listenets == null){
listenets = new HashSet();
}
listenets.add(e);
}
//刪除監(jiān)聽
public void removeListener(EventListener e){
if (listenets != null){
listenets.remove(e);
}
}
/** 根據(jù)觸發(fā)的條件進(jìn)行事件的執(zhí)行
* @param state 觸發(fā)條件
*/
protected void fireEvent(String state){
if (listenets != null){
MyEvent event = new MyEvent(this,state);
notifyA(event);
}
}
/**
* 事件具體的執(zhí)行
* @param e 定義的事件
*/
public void notifyA(MyEvent e){
Iterator iterator = listenets.iterator();
while (iterator.hasNext()){
//實(shí)例監(jiān)聽器對(duì)象,并調(diào)用監(jiān)聽器的方法
EventListener evt = (EventListener) iterator.next();
evt.handEvent(e);
}
}
}
- 定義監(jiān)聽器,把事件作為參數(shù),也是和觀察者模式的區(qū)別
public interface EventListener{
//把事件對(duì)象作為參數(shù)
void handEvent(MyEvent e);
}
- 實(shí)現(xiàn)listener 接口,定義業(yè)務(wù)邏輯
public class Mylistener implements EventListener {
@Override
public void handEvent(MyEvent e) {
//todo 使用if做邏輯控制
if (e.getState() != null && e.getState() == "hi" ){
System.out.println(" hi ,java事件機(jī)制");
}
}
}
public class Mylistener2 implements EventListener {
@Override
public void handEvent(MyEvent e) {
if (e.getState() != null && e.getState().equals("h")){
System.out.println(" 我是被 "+e.getState()+" 觸發(fā)的");
}
}
}
- 測(cè)試
public static void main(String[] args){
//事件源
EventSource source = new EventSource();
/*
* 當(dāng)有需要的事件要被觸發(fā)時(shí)就需要編寫并注冊(cè) 相關(guān)的listener
* 這也是事件的弊端
* */
source.addEventListener(new Mylistener());
source.addEventListener(new Mylistener2());
//根據(jù)狀態(tài)觸發(fā)
source.fireEvent("hi");
source.fireEvent("h");
}