設計模式之觀察者模式(ObserverPattern)
一.觀察者模式(ObserverPattern)
在生活中會遇到很多使用觀察者模式的案例,比如股票跌漲後,股民作爲觀察者會對股票進行交易;客人來敲門,門鈴響了之後管家就會去開門等等。其中觀察者與被觀察者都有相互聯繫。
注: 接下來爲了方便理解類之間的設計關係,採用StartUML畫類圖,用C++編程
二.編程實例
1.進制轉換器
其中需要注意的有兩個點:
1.Subject類與Observer類之間相互關聯。
2.因爲只要Subject模型一改變,所有的Observer派生類都要改變,所以Observer抽象類用static修飾Subject變量
3.一旦創建了新的Observer派生類,首先賦值Subject(被觀察的類),然後將自己添加到Subject的通知對象中
類圖設計
:
程序
:
#include <iostream>
#include <memory>
#include <vector>
#include <cstdio>
using namespace std;
/*
設計模式:觀察者模式(Observer Pattern)
*/
/********************************觀察者Observer***************************/
class Subject;
class Observer
{
public:
static Subject* _subject;//因爲只要Subject一改變,所有的Observer派生類都要改變,所以用static修飾
public:
virtual ~Observer(){}
virtual void update() = 0;
};
Subject* Observer::_subject;
/********************************定義被觀察的對象Subject***************************/
class Subject
{
vector<Observer*> _observers;
size_t _number;
public:
size_t getNumber()const
{
return _number;
}
void setNumber(size_t number)
{
_number = number;
notifyAllObservers();
}
void attachObserver(Observer* observer)
{
_observers.push_back(observer);
}
void notifyAllObservers()
{
for (auto& e: _observers)
e->update();
}
};
/********************************Observer派生類***************************/
//8進制
class OctObserver:public Observer
{
public:
OctObserver(Subject* subject)
{
_subject = subject;
_subject->attachObserver(this);//一旦創建了新的Observer派生類,首先賦值Subject(被觀察的類),然後將自己添加到Subject的通知對象中
}
void update()
{
cout << "Oct:" << oct << _subject->getNumber() << endl;
}
};
//10進制
class DecObserver:public Observer
{
public:
DecObserver(Subject* subject)
{
_subject = subject;
_subject->attachObserver(this);
}
void update()
{
cout << "Dec:" << dec << _subject->getNumber() << endl;
}
};
//16進制
class HexObserver:public Observer
{
public:
HexObserver(Subject* subject)
{
_subject = subject;
_subject->attachObserver(this);
}
void update()
{
cout << "Hex:" << hex << _subject->getNumber() << endl;
}
};
int main()
{
unique_ptr<Subject> up(new Subject());
new DecObserver(up.get());
new HexObserver(up.get());
new OctObserver(up.get());
up->setNumber(10);
cout << "-----------------------" << endl;
up->setNumber(16);
return 0;
}
2.客人敲門案例
假設有客人,門鈴,嬰兒,護工四個類。
門鈴是Subject被觀察者,嬰兒和護士是觀察者。
嬰兒和護工隨時觀察門鈴,客人按動鈴鐺的時候,嬰兒開始哭,護工去開門。
類圖設計
:
程序
:
#include <iostream>
#include <memory>
#include <list>
#include <cstdio>
#include <algorithm>
using namespace std;
/*
觀察者模式實際案例:
假設有客人,門鈴,嬰兒,護工四個類。
門鈴是Subject被觀察者,嬰兒和護士是觀察者。
嬰兒和護工隨時觀察門鈴,客人按動鈴鐺的時候,嬰兒開始哭,護工去開門。
下面是案例設計的UML類圖,完成程序:
*/
/***********************Observer AND Subject*****************************/
class Observer
{
public:
virtual string name() const = 0;
virtual void update() = 0;
};
class Subject
{
public:
virtual void attach(Observer *) = 0;
virtual void detach(Observer *) = 0;
virtual void notify() = 0;
};
/***********************Baby AND Nurse*****************************/
class Baby : public Observer
{
string _name;
public:
Baby(string name)
: _name(name)
{
}
string name() const { return _name; }
void update()
{
cout << _name << " is Crying" << endl;
}
};
class Nurse : public Observer
{
string _name;
public:
Nurse(string name)
: _name(name)
{
}
string name() const { return _name; }
void update()
{
cout << "Nurse is opening the door" << endl;
}
};
/***********************Ring*****************************/
class Ring : public Subject
{
list<Observer *> _oblist;
// list<Observer *> _goldenlist; //黃金會員
// list<Observer *> _diamondlist; //鑽石會員
public:
void attach(Observer *ob)
{
auto it = find(_oblist.begin(), _oblist.end(), ob);
if (it == _oblist.end())
{
_oblist.push_back(ob);
}
}
void detach(Observer *ob)
{
auto it = find(_oblist.begin(), _oblist.end(), ob);
if (it != _oblist.end())
{
_oblist.erase(it);
}
}
void alarm()
{
notify();
}
void notify()
{
for (auto &e : _oblist)
e->update();
}
};
/***********************Ring*****************************/
class Guest
{
string _name;
public:
Guest(string name)
: _name(name)
{
}
string name() const { return _name; }
void knock(Ring* ring)
{
cout << _name << " is knocking the door" << endl;
ring->alarm();
}
};
int main()
{
unique_ptr<Observer> baby1(new Baby("haha"));
unique_ptr<Observer> baby2(new Baby("bala"));
unique_ptr<Observer> nurse(new Nurse("gj"));
unique_ptr<Ring> ring(new Ring());
ring->attach(baby1.get());
ring->attach(baby2.get());
ring->attach(nurse.get());
Guest guest("Uncle");
guest.knock(ring.get());
ring->detach(baby2.get());
guest.knock(ring.get());
return 0;
}