前言代码实现-单线程下的观察者模式
被观察者-基类实现被观察者-派生类实现观察者-基类实现观察者-派生类实现测试 代码实现-多线程下的观察者模式
观察者和被观察者的基类被观察者-派生类观察者-派生类测试多线程下的观察者模式-改进
前言观察者概念介绍见:观察者模式
完整代码见仓库:observer_mode
代码实现-单线程下的观察者模式被观察者:产品的成本。
观察者:该产品在路边摊的价格和商场的价格。
订阅-发布:当产品成本改变时,产品在不同销售地点的价格随之改变。
(2.1.0)被观察者,传递给观察者,的内容(被观察者发布的内容,观察者订阅的内容)
(2.1.1)选定存储被观察者的数据结构。
(2.1.2)该抽象基类定义的接口-操作存储被观察者数据结构的方法。
观察者Observer对象,通过registerObs函数,将自己注册到被观察者Subject的数据中心中。当数据改变的时候,Subject对象调用notifyObs函数,通知所有的观察者对象。deleteObs用于删除注册的观察者。
class Subject {
protected:
double price=0; // (2.1.0)
std::vector observers; // (2.1.1)
public:
virtual void registerObs(Observer* ) = 0; // (2.1.2)
virtual void deleteObs(Observer* ) = 0;
virtual void notifyObs() = 0;
virtual ~Subject() = default;
double setPrice(double p) {
price = p;
}
int getPrice() {
return price;
}
};
被观察者-派生类实现
实现上面抽象基类定义的接口。(被观察者可以不需要基类,可以直接在上面进行具体实现。这里为了清晰显示接口,故分开来写。)
class Product : public Subject {
public:
void registerObs(Observer* obs) override{
observers.push_back(obs);
}
void deleteObs(Observer* obs) override{
auto it = std::find(observers.begin(), observers.end(), obs);
if(it != observers.end())
observers.erase(it);
}
void notifyObs() override {
for(auto obs : observers){
obs->update(price);
}
}
};
观察者-基类实现
(2.3.1)观察者基类。
class Observer {
protected:
double price=0;
public:
virtual void update(double) = 0;
virtual ~Observer() = default;
};
观察者-派生类实现
update函数:派生类接收从被观察者传递过来的内容,并进行处理。
// 派生类,东西在地摊的价格-随着成本的波动
class streetProduct : public Observer {
public:
void update(double cost) override {
price = cost*1.5;
std::cout<<"The procude's cost is "<
测试
被观察者product将obs1和obs2注册到自己的数据结构中。当它们关心的数据通过setPrice方法,发生改变的时候,product通知所有的订阅者(obs1,obs2)。
int main(void){
Product product;
streetProduct obs1;
marketProduct obs2;
product.registerObs(&obs1);
product.registerObs(&obs2);
product.setPrice(100); // 成本设置为100
product.notifyObs(); // 通知obs1和obs2
}
运行结果如下:
The procude's cost is 100;When it appear in street stall, It's price is 150
The procude's cost is 100;When it appear in market, It's price is 180
代码实现-多线程下的观察者模式
上面的代码不能用于多线程,因为不能让多线程操作一个没有同步的数据结构。
上面的代码是被观察者调用注册方法,将观察者注册进去。或许应该,被观察者公布注册接口,观察者自己将自己注册进去。
观察者和被观察者的基类
(3.1.1)表示被观察者-观察者之间传递的数据类型为double。
// 观察者基类
class Observer {
public:
virtual void update(double) = 0; //(3.1.1)
virtual ~Observer() = default;
};
// 基类(被观察者)
class Subject {
protected:
std::vector observers;
public:
virtual void registerObs(Observer* ) = 0;
virtual void deleteObs(Observer* ) = 0;
virtual void notifyObs() = 0;
virtual ~Subject() = default;
};
被观察者-派生类
(3.2.1)mutex类是能用于保护共享数据免受从多个线程同时访问的同步原语。
(3.2.2)创建lock_guard对象时,它试图接收给定互斥的所有权。控制离开创建 lock_guard 对象的作用域时,销毁 lock_guard 并释放互斥。
class Product : public Subject {
private:
int price=0;
std::mutex mutex_; //(3.2.1)
public:
void registerObs(Observer* obs) override{
std::lock_guard guard(mutex_); // (3.2.2)
observers.push_back(obs);
}
void deleteObs(Observer* obs) override{
std::lock_guard guard(mutex_);
auto it = std::find(observers.begin(), observers.end(), obs);
if(it != observers.end())
observers.erase(it);
}
void notifyObs() override {
std::lock_guard guard(mutex_);
for(auto obs : observers){
obs->update(price);
}
}
void setPrice(int p) {
price = p;
}
int getPrice() {
return price;
}
};
观察者-派生类
(3.3.1)观察者在构造函数中,将自己注册到观察者中。
(3.3.2)观察者在最后一层的派生类的析构函数中,将自己从被观察者中删除。(如果在基类析构函数中进行注册者的删除,是有问题的。eg:派生类先析构了,但是基类还没来得及析构,此时被观察者调用了update函数,这是很糟糕的。但是从类的功能划分来说,析构的任务似乎放在基类中更合适。)
// 派生类,东西在地摊的价格-随着成本的波动
class streetProduct : public Observer {
private:
double price=0;
Product& product;
public:
streetProduct(Product& p): product(p) { //(3.3.1)
product.registerObs(this);
};
void update(double cost) override {
price = cost*1.5;
std::cout<<"The procude's cost is "<
测试
(3.4.1)类thread表示单个执行线程。
由于锁的存在,该多线程可以安全运行。
void addObs1(Product& p){
streetProduct obs1(p);
p.setPrice(100);
p.notifyObs();
}
int main(void){
Product product; // 提供存储功能。当数据变动时,提供通知。通知的具体形式,由各自的观察者实现
thread t1(addObs1,ref(product)); //(3.4.1)
marketProduct obs2(product); // 创建时候,构造函数将自己注册到被观察者对象中;消除的时候,析构函数将自己从被观察者对象中删除
product.setPrice(100); // 成本设置为100
product.notifyObs(); // 通知obs1和obs2
t1.join();
}
运行结果:
The procude's cost is 100;When it appear in market, It's price is 180
The procude's cost is 100;When it appear in market, It's price is 180
The procude's cost is 100;When it appear in street stall, It's price is 150
多线程下的观察者模式-改进
核心思路(使用智能指针):将被观察者中,存储注册观察者的数据结构,改成std::vector>。
参考代码:linux多线程服务器编程-observe
参考视频:C++新手向:手把手教你写一个线程安全的Observer模式-B站
今天1/30日,中午和同学去吃了华莱士,晚上吃年夜饭。^_^。



