何時(shí)使用觀察者模式
1媚送、觸發(fā)聯(lián)動(dòng):當(dāng)修改目標(biāo)狀態(tài)時(shí)就會(huì)觸發(fā)相應(yīng)的通知呀癣,然后會(huì)循環(huán)調(diào)用所有注冊的觀察者對象的相應(yīng)方法镣屹。
2凉馆、建議在下面三種情況下使用觀察者模式:
1.當(dāng)一個(gè)抽象模型有兩個(gè)方面,其中一個(gè)方面的操作依賴于另一個(gè)方面的狀態(tài)變化時(shí)
2.如果在封蓋一個(gè)對象的時(shí)候攘已,需要同時(shí)連帶改變其他的對象炮赦,而且不知道究竟應(yīng)該有對少對象需要被連帶改變
3.當(dāng)一個(gè)對象必須通知其他的對象,但是你又希望這個(gè)對象和其他被他通知的對象是松散耦合的
實(shí)現(xiàn)方式一:觀察者模式 經(jīng)典模版
觀察者模式的結(jié)構(gòu):Subject(被觀察者或者說是目標(biāo)類):要有添加觀察者样勃,刪除觀察者吠勘,和提醒觀察者(當(dāng)被觀察者的狀態(tài)發(fā)生改變的時(shí)候調(diào)用這個(gè)方法)的方法,Observe(觀察者):要有更新方法(當(dāng)觀察者狀態(tài)發(fā)生改變調(diào)用提醒方法后觀察者通過更新方法來做出不同響應(yīng)(動(dòng)作))峡眶。
ConcreteSubject是Subject接口的實(shí)現(xiàn)類
ConcreteObserver是Observer接口的實(shí)現(xiàn)類
目標(biāo)(被觀察者):
/**
* 目標(biāo)對象,它知道觀察它的觀察者,并提供注冊(添加)和刪除觀察者的接口
*/
public class Subject {
? ? // 用來保存注冊的觀察者對象
? ? private List<Observer> observers = new ArrayList<Observer>();
? ? // attach detach notifyObservers
? ? // 把訂閱天氣的人添加到訂閱者列表中
? ? public void attach(Observer observer) {
? ? ? ? observers.add(observer);
? ? }
? ? /**
? ? * 刪除集合中的指定觀察者
? ? * @param observer
? ? */
? ? public void detach(Observer observer) {
? ? ? ? observers.remove(observer);
? ? }
? ? /**
? ? * 通知所有注冊的觀察者對象
? ? */
? ? protected void notifyObservers() {
? ? ? ? for (Observer observer : observers) {
? ? ? ? ? ? observer.update(this);
? ? ? ? }
? ? }
}
目標(biāo)(被觀察者)具體實(shí)現(xiàn):
/**
* 具體的目標(biāo)對象,負(fù)責(zé)把有關(guān)狀態(tài)存入到相應(yīng)的觀察者對象中
*/
public class ConcreteSubject extends Subject {
? ? // 目標(biāo)對象的狀態(tài)
? ? private String Content;
? ? public String getSubjectState() {
? ? ? ? return Content;
? ? }
? ? public void setSubjectState(String Content) {
? ? ? ? this.Content = Content;
? ? ? ? // 內(nèi)容有了,通知所有的訂閱的人
? ? ? ? this.notifyObservers();
? ? }
}
觀察者接口:
/**
* 這是一個(gè)觀察者接口,定義一個(gè)更新的接口給那些在目標(biāo)發(fā)生改變的時(shí)候被通知的對象
*/
public interface Observer {
? ? /**
? ? * 更新的接口 傳入目標(biāo)對象,方便獲取相應(yīng)的目標(biāo)對象的狀態(tài)
? ? * @param subject
? ? */
? ? void update(Subject subject);
}
觀察者接口的具體實(shí)現(xiàn):
/**
* 具體的觀察者對象,實(shí)現(xiàn)更新的方法,使自身的狀態(tài)和目標(biāo)的狀態(tài)保持一致
*/
public class ConcreteObserver implements Observer {
? ? // 觀察者的名字,是誰收到了這個(gè)訊息
? ? private String observerName;
? ? // 觀察者的狀態(tài),這個(gè)消息從目標(biāo)處獲取
? ? private String observerState;
? ? // 提醒的內(nèi)容
? ? private String remindTing;
? ? /**
? ? * 獲取目標(biāo)類的狀態(tài)同步到觀察者的狀態(tài)中
? ? */
? ? @Override
? ? public void update(Subject subject) {
? ? ? ? observerState = ((ConcreteSubject) subject).getSubjectState();
? ? ? ? System.out.println(observerName + "收到了, " + observerState + " , " + remindTing);
? ? }
? ? public String getObserverName() {
? ? ? ? return observerName;
? ? }
? ? public void setObserverName(String observerName) {
? ? ? ? this.observerName = observerName;
? ? }
? ? public String getRemindTing() {
? ? ? ? return remindTing;
? ? }
? ? public void setRemindTing(String remindTing) {
? ? ? ? this.remindTing = remindTing;
? ? }
}
測試類:
? ? public static void main(String[] args) {
? ? ? ? // 1.創(chuàng)建目標(biāo)
? ? ? ? ConcreteSubject weather = new ConcreteSubject();
? ? ? ? // 2.創(chuàng)建觀察者
? ? ? ? ConcreteObserver observerGirl = new ConcreteObserver();
? ? ? ? observerGirl.setObserverName("小明的女朋友");
? ? ? ? observerGirl.setRemindTing("是我們的第一次約會(huì),地點(diǎn)街心公園,不見不散哦");
? ? ? ? ConcreteObserver observerMum = new ConcreteObserver();
? ? ? ? observerMum.setObserverName("老媽");
? ? ? ? observerMum.setRemindTing("是一個(gè)購物的好日子,明天去天虹掃貨");
? ? ? ? // 3.注冊觀察者
? ? ? ? weather.attach(observerGirl);
? ? ? ? weather.attach(observerMum);
? ? ? ? // 4.目標(biāo)發(fā)布天氣
? ? ? ? weather.setSubjectState("#明天天氣晴朗,藍(lán)天白云,氣溫28度#");
? ? }
實(shí)現(xiàn)方式二:利用Java提供的觀察者實(shí)現(xiàn) 觀察者模式
Java 實(shí)現(xiàn) VS 自己實(shí)現(xiàn)的對比四點(diǎn):
(1)不需要再定義觀察者和目標(biāo)接口(JDK已經(jīng)定義)剧防。
(2)具體的目標(biāo)實(shí)現(xiàn)里面不需要再維護(hù)觀察者的注冊信息,Java中的Observable類里面已經(jīng)實(shí)現(xiàn)辫樱。
(3)觸發(fā)通知的方式有一點(diǎn)變化峭拘,要先調(diào)用setChanged方法,這個(gè)是Java為了幫助實(shí)現(xiàn)更精確的觸發(fā)控制而提供的功能狮暑。
(4)具體觀察者的實(shí)現(xiàn)里面鸡挠,update方法其實(shí)能同時(shí)支持推模型和拉模型,這個(gè)Java在定義的時(shí)候搬男,已經(jīng)考慮拣展。
目標(biāo)(被觀察者):
/**
* 被觀察者的具體實(shí)現(xiàn)
*/
public class ConcreteSubject extends Observable {
? ? // 變化的具體內(nèi)容
? ? private String content;
? ? public String getContent() {
? ? ? ? return content;
? ? }
? ? public void setContent(String content) {
? ? ? ? this.content = content;
? ? ? ? // 發(fā)生變化,就要通知所有的觀察者
? ? ? ? // 注意在通知之前,在用Java中的Observer模式的時(shí)候,下面這句話不可少
? ? ? ? this.setChanged();
? ? ? ? // 然后主動(dòng)通知,這里我們先用推的方式
? ? ? ? this.notifyObservers(content);
? ? ? ? // 如果是拉的方式,我們就調(diào)用
? ? ? ? // this.notifyObservers();
? ? }
}
觀察者:
/**
* 具體的觀察者對象
*/
public class ConcreteObserver implements Observer{
? ? //觀察者名稱的變量
? ? private String observerName;
? ? @Override
? ? public void update(Observable o, Object arg) {
? ? ? ? //第一種是推的方式
? ? ? ? System.out.println(observerName+"收到了消息,目標(biāo)推送過來的是"+arg);
? ? ? ? //第二種是拉的方式
? ? ? ? System.out.println(observerName+"收到了消息,主動(dòng)到目標(biāo)對象中去拉,拉的內(nèi)容是"+((ConcreteSubject)o).getContent());
? ? }
? ? public String getObserverName() {
? ? ? ? return observerName;
? ? }
? ? public void setObserverName(String observerName) {
? ? ? ? this.observerName = observerName;
? ? }
}
測試類:
public static void main(String[] args) {
? ? ? ? // 創(chuàng)建一個(gè)目標(biāo),也就是被觀察者
? ? ? ? ConcreteSubject subject = new ConcreteSubject();
? ? ? ? // 創(chuàng)建小明的女朋友作為觀察者
? ? ? ? ConcreteObserver girl = new ConcreteObserver();
? ? ? ? girl.setObserverName("小明的女朋友");
? ? ? ? // 創(chuàng)建小明的老媽作為觀察者
? ? ? ? ConcreteObserver mum = new ConcreteObserver();
? ? ? ? mum.setObserverName("小明的老媽");
? ? ? ? // 注冊觀察者
? ? ? ? subject.addObserver(girl);
? ? ? ? subject.addObserver(mum);
? ? ? ? // 目標(biāo)更新天氣情況了
? ? ? ? subject.setContent("天氣晴,氣溫28度");
? ? }
觀察者優(yōu)缺點(diǎn)
1、觀察者模式的優(yōu)點(diǎn):
(1)觀察者模式實(shí)現(xiàn)了觀察者和目標(biāo)之間的抽象耦合
(2)觀察者模式實(shí)現(xiàn)了動(dòng)態(tài)聯(lián)動(dòng)
(3)觀察者模式支持廣播通信
2缔逛、觀察者模式的缺點(diǎn):可能會(huì)引起無謂的操作备埃。
實(shí)現(xiàn)方式三:區(qū)別對待觀察者場景問題 (靈活定制觀察者)
區(qū)別觀察者模式是,目標(biāo)父類不實(shí)現(xiàn)通知方法褐奴,在子類中實(shí)現(xiàn)有區(qū)別的通知方法瓜喇。
區(qū)別對待的觀察者模型中和通用觀察者模型的區(qū)別在于:要根據(jù)不同的觀察者來進(jìn)行不同的推送,所以區(qū)別在于目標(biāo)類中的通知更新方法需要在具體的目標(biāo)類中進(jìn)行實(shí)現(xiàn)歉糜。(因?yàn)樾枰鶕?jù)不同的情況進(jìn)行更新,所以需要在具體的目標(biāo)類中實(shí)現(xiàn)剛剛那個(gè)方法)
區(qū)別對待觀察者望众,邏輯判斷讓觀察者實(shí)現(xiàn)更符合邏輯匪补,定義到接口觀察者中,setRule()烂翰,讓小明女朋友和老媽自己去定義
目標(biāo)(被觀察者)的抽象方法:
public abstract class Subject {
? ? // 用來保存注冊的觀察者對象
? ? public List<Observer> observers = new ArrayList<Observer>();
? ? // attach detach abstract notifyObservers
? ? // 把觀察者添加到訂閱者列表中
? ? 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 ConcreteSubject extends Subject {
? ? // "晴天" "下雨" "下雪"
? ? // 目標(biāo)對象的狀態(tài)
? ? private String Content;
? ? @Override
? ? protected void notifyObservers() {
? ? ? ? // 循環(huán)所有注冊的觀察者
? ? ? ? for (Observer observer : observers) {
? ? ? ? ? ? // 情況之一:
? ? ? ? ? ? // 如果天氣是晴天夯缺,按照小明的女朋友需要下雨的條件,小明的老媽需要下雨或下雪的條件甘耿,則她們倆就都不需要通知了踊兜。
? ? ? ? ? ? // 情況之二:
? ? ? ? ? ? // 如果天氣是下雨,則小明的女朋友需要通知佳恬,而小明的老媽也需要通知捏境。
? ? ? ? ? ? // 情況之三:
? ? ? ? ? ? // 如果天氣是下雪于游,則只需要通知小明的老媽。
? ? ? ? ? ? if("下雨".equals(this.getContent())){
? ? ? ? ? ? ? ? if("小明的女朋友".equals(observer.getObserverName())){
? ? ? ? ? ? ? ? ? ? observer.update(this);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? if("小明的老媽".equals(observer.getObserverName())){
? ? ? ? ? ? ? ? ? ? observer.update(this);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? ? ? if("下雪".equals(this.getContent())){
? ? ? ? ? ? ? ? if("小明的老媽".equals(observer.getObserverName())){
? ? ? ? ? ? ? ? ? ? observer.update(this);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? }
? ? public String getContent() {
? ? ? ? return Content;
? ? }
? ? public void setContent(String content) {
? ? ? ? Content = content;
? ? ? ? this.notifyObservers();
? ? }
}
觀察者的接口:
/**
* 定義一個(gè)更新的接口方法給那些在目標(biāo)發(fā)生改變的時(shí)候被通知的觀察者對象調(diào)用
*/
public interface Observer {
? ? // 更新的接口
? ? public void update(Subject subject);
? ? // 設(shè)置觀察者名稱
? ? public void setObserverName(String observerName);
? ? // 取得觀察者名稱
? ? public String getObserverName();
}
觀察者的具體實(shí)現(xiàn):
public class ConcreteObserver implements Observer {
? ? // 觀察者的名稱
? ? private String observerName;
? ? // 天氣情況的內(nèi)容
? ? private String content;
? ? // 提醒的內(nèi)容
? ? private String remindThing;
? ? @Override
? ? public void update(Subject subject) {
? ? ? ? content = ((ConcreteSubject) subject).getContent();
? ? ? ? System.out.println(observerName + "收到了<" + content + ">," + remindThing);
? ? }
? ? @Override
? ? public void setObserverName(String observerName) {
? ? ? ? this.observerName = observerName;
? ? }
? ? @Override
? ? public String getObserverName() {
? ? ? ? return observerName;
? ? }
? ? public String getContent() {
? ? ? ? return content;
? ? }
? ? public void setContent(String content) {
? ? ? ? this.content = content;
? ? }
? ? public String getRemindThing() {
? ? ? ? return remindThing;
? ? }
? ? public void setRemindThing(String remindThing) {
? ? ? ? this.remindThing = remindThing;
? ? }
}
測試類:
public static void main(String[] args) {
? ? ? ? // 1.創(chuàng)建目標(biāo)
? ? ? ? ConcreteSubject weather = new ConcreteSubject();
? ? ? ? // 2.創(chuàng)建觀察者
? ? ? ? ConcreteObserver observerGirl = new ConcreteObserver();
? ? ? ? observerGirl.setObserverName("小明的女朋友");
? ? ? ? observerGirl.setRemindThing("下雨了,安靜的呆在家里吧");
? ? ? ? ConcreteObserver observerMum = new ConcreteObserver();
? ? ? ? observerMum.setObserverName("小明的老媽");
? ? ? ? observerMum.setRemindThing("不管下雨還是下雪,我都不出門了");
? ? ? ? // 3.注冊觀察者
? ? ? ? weather.attach(observerGirl);
? ? ? ? weather.attach(observerMum);
? ? ? ? // 4.目標(biāo)發(fā)布天氣
? ? ? ? weather.setContent("下雪");
? ? }