2018-09-21 20:57:03ios
觀察者模式又叫作發佈-訂閱(Publish/Subscribe)模式。它定義了一種一對多的依賴關係,讓多個觀察者對象同時監聽某一個主題對象。這個主題對象在狀態發生變化時,會通知全部的觀察者對象,使它們可以自動更新本身。框架
Subject類(通常叫作主題或者抽象統治者,一般用一個抽象類實現),它把全部對觀察者對象的引用保存在一個集合裏,每一個主題均可以有任何數量的觀察者。抽象主題提供一系列方法,能夠增長和刪除觀察者對象。ide
Observer類,抽象觀察者,爲全部的具體觀察者定義一個接口,在獲得主題的通知時更新本身。更新方法一般包含一個Update()方法。學習
ConcreteSubject類,具體主題或者具體通知者,將有關狀態存入具體觀察者對象;在具體主題的內部狀態改變時,給全部登記過的觀察者發出通知。spa
ConcreteObserver類,具體觀察者,實現抽象觀察者角色所要求的更新接口,以便使自己的狀態與主題的狀態相協調。一般用一個具體子類實現。3d
還讀書的時候,有節課咱們都很喜歡,就是自習課,由於你們能夠在自習課上作不少有意思的事情,好比睡覺、看小說、玩手機等等。但是萬一被老師抓到了那可就麻煩了,玩手機的手機會被沒收,看小說的小說也要上交,要是睡覺,那你中大獎了,可能要被罰站。因此怎麼辦呢,若是老師來了,有人通知,那麼就不會被罰了,只要及時作好認真學習的樣子(^_^)。這裏就隱含了一個可使用觀察者模式的場景,在這個場景中,老師就是要訂閱的主題,班級裏沒好好學習的學生就是觀察者。code
1.抽象觀察者(每一個觀察者都必需要有一個Update方法,或者功能相似的方法,方法的功能就是根據訂閱主題的變化,本身作出相應的動做)(UML中的Observer)server
#ifndef OBSERVER_H_ #define OBSERVER_H_ #include <string> class Observer { public: virtual void update(std::string strTeacherName) = 0; Observer() = default; virtual ~Observer() = default; }; #endif
2.具體觀察者對象
#ifndef PLAYINGSTUDENT_H_ #define PLAYINGSTUDENT_H_ #include "Observer.h" #include <iostream> #include <string> class PlayingStudent:public Observer { public: void update(const std::string strTeacherName) override; PlayingStudent(const std::string strStudentName) : m_strNameOfStudent(strStudentName){}; ~PlayingStudent() = default; private: std::string m_strNameOfStudent; }; #endif #include "PlayingStudent.h" void PlayingStudent::update(const std::string strTeacherName) { std::cout << "Subject: "<< strTeacherName << " is coming!" << std::endl; std::cout << "Observer:" << m_strNameOfStudent << " :Where ?" << std::endl; } #ifndef SLEEPINGSTUDENT_H_ #define SLEEPINGSTUDENT_H_ #include "Observer.h" #include <iostream> #include <string> class SleepingStudent:public Observer { public: void update(const std::string strTeacherName) override; //Construct Function: or you can declare it as Observer(ConcreteSubject objConcretestruct,const std::string strName) for get state of Subject SleepingStudent(const std::string strStudentName):m_strNameOfStudent(strStudentName){}; ~SleepingStudent() = default; private: std::string m_strNameOfStudent; }; #endif #include "SleepingStudent.h" void SleepingStudent::update(const std::string strTeacherName) { std::cout << m_strNameOfStudent << "," << strTeacherName << "is comming,weak up" << std::endl; }
3.抽象主題blog
#ifndef SUBJECT_H_ #define SUBJECT_H_ #include "Observer.h" class Subject { public: virtual void attach(Observer *) = 0; //Add Observer virtual void detach(Observer *) = 0; //Remove Observer virtual void notify() = 0; //Note Observer Subject() = default; virtual ~Subject() = default; }; #endif
4.具體主題
#ifndef CONCRETESUBJECT_H_ #define CONCRETESUBJECT_H_ #include "Subject.h" #include "Observer.h" #include <list> #include <string> class ConcreteSubject : public Subject { public: void attach(Observer* objObserver) override; void detach(Observer *objObserver) override; void notify() override; void setTeacherName(const std::string strName) { m_strNameOfTeacher = strName; } ConcreteSubject() = default; ~ConcreteSubject() = default; private: std::string m_strNameOfTeacher; std::list<Observer* > m_listObserver; }; #endif #include "ConcreteSubject.h" void ConcreteSubject::attach(Observer* objObserver) { m_listObserver.push_back(objObserver); } void ConcreteSubject::detach(Observer *objObserver) { m_listObserver.remove(objObserver); } void ConcreteSubject::notify() { for(auto value : m_listObserver) { value->update(m_strNameOfTeacher); } }
5.client
#include "ConcreteSubject.h" #include "SleepingStudent.h" #include "PlayingStudent.h" using namespace std; int main(int argc,char *argv[]) { SleepingStudent objStudentA("H&T"); ConcreteSubject objSubject; objSubject.setTeacherName("Mr Su"); objSubject.attach(&objStudentA); SleepingStudent objStudentB("M"); objSubject.attach(&objStudentB); PlayingStudent objPlayingStudentA("N"); objSubject.attach(&objPlayingStudentA); PlayingStudent objPlayingStudentB("Z"); objSubject.attach(&objPlayingStudentB); objSubject.detach(&objPlayingStudentB); objSubject.notify(); return(1); }
適用場景:
將一個系統分隔成一系列相互協做的類帶來的一個反作用是:須要維護相關對象的一致間的一致性。咱們不但願爲了維護一致性而使得各種緊密耦合,這樣會給維護、擴展和重用都帶來不便。而觀察者模式的關鍵對象是主題Subject和觀察者Observer,一個Subject能夠有任意數目的依賴它的Observer,一旦Subject的狀態發生了改變,全部的Observe均可以獲得通知。Subject發出通知時並不須要知道誰是它的觀察者,也就是說,具體觀察者是誰,它根本不須要知道。而任何一個具體觀察者也不須要知道其它觀察者的存在。
那麼當一個對象的改變須要同時改變其它對象的時候,而且它不知道具體有多少對象有待改變時,應該考慮使用觀察者模式。就是說,一個抽象模型有兩個方面,其中一方面依賴於另外一方面,這時用觀察者模式能夠將這二者封裝在獨立的對象中使它們各自獨立的改變和複用。
觀察者所作的事情就是解耦合,讓耦合的雙方都依賴於抽象,而不依賴具體,從而使得各自的變換都不會影響另外一邊的變化。
優勢:
1.一個抽象模型的兩個方面相互依賴,使用觀察者模式將其解耦爲觀察者和主題,配合使用虛基類,使得二者的耦合度下降(依賴抽象而不依賴具體),這能使雙方的變化能夠獨自進行以及複用。
缺點:
1.若是一個主題有不少個觀察者,將全部觀察者挨個通知一遍會消耗時間
2.觀察者模式沒有一個機制讓觀察者知道變化是怎麼發生的,而僅知道發生了這樣的變化。
3.觀察目標和觀察者之間若是有循環依賴的話(觀察者和觀察目標彼此擁有一個存儲對方對象的集合),觀察目標會觸發兩個模塊之間的循環調用,這有可能會引起系統崩潰
4.雖然經過抽象觀察者和抽象主題,解除了實現之間的依賴,可是依然存在抽象觀察和抽象主題這樣的依賴,對一個已經開發好的框架,假設這兩個缺乏其一,那麼就不能添加主題的通知功能,另外觀察者接到通知後的方法名可能有不少種,可是通知者和觀察者彼此之間並不知道, 此時能夠由客戶端來決定通知誰。