文章目录
- 什么是观察者模式
- vtk是如何实现的观察者模式.AddObserver
- 什么时候使用观察者模式?什么使用订阅发布者模式?
- 观察者模式的实现
- 订阅发布者的实现
- 总结
- 知识补充:
什么是观察者模式
用于在对象之间建立一对多的依赖关系,当一个对象的状态发生变化时,其所依赖的所有对象都会收到通知并自动更新。
vtk是如何实现的观察者模式.AddObserver
在VTK(Visualization Toolkit)中,观察者模式通过AddObserver方法来实现。AddObserver方法允许一个对象(观察者)注册自己以便接收另一个对象(被观察者)的通知。被观察者对象在发生特定事件或状态变化时会调用已注册的观察者的相应方法,以便进行相应的处理或更新。
#include <vtkSmartPointer.h>
#include <vtkObject.h>
#include <vtkCommand.h>
#include <iostream>// 自定义观察者类
class CustomObserver : public vtkCommand
{
public:static CustomObserver* New(){return new CustomObserver;}virtual void Execute(vtkObject* caller, unsigned long eventId, void* callData){if (caller){std::cout << "Received event: " << caller->GetClassName() << ", Event ID: " << eventId << std::endl;}}
};int main()
{// 创建一个被观察者对象vtkSmartPointer<vtkObject> observedObject = vtkSmartPointer<vtkObject>::New();// 创建一个观察者对象vtkSmartPointer<CustomObserver> observer = vtkSmartPointer<CustomObserver>::New();// 注册观察者对象到被观察者对象observedObject->AddObserver(vtkCommand::AnyEvent, observer);// 触发事件,通知观察者对象observedObject->InvokeEvent(vtkCommand::ModifiedEvent);return 0;
}
/*
我们首先创建了一个被观察者对象observedObject和一个自定义的观察者类CustomObserver。然后,使用AddObserver方法将观察者对象observer注册到被观察者对象上,通过指定事件ID(vtkCommand::AnyEvent表示任何事件)和观察者对象来建立它们之间的关联。最后,我们调用InvokeEvent方法来触发事件并通知观察者对象。当事件被触发时,执行观察者类中的Execute方法,打印出相关信息。
*/
什么时候使用观察者模式?什么使用订阅发布者模式?
- 使用观察者模式的情况包括:
1.当一个对象的改变需要同时影响其他多个对象,并且不希望显式地指定这些对象时,可以使用观察者模式。
2.当一个对象的改变需要通知其他对象,但是不需要知道具体的接收者时,可以使用观察者模式。
3.当一个对象需要在改变时通知一组对象而无需知道具体对象时,可以使用观察者模式。
订阅发布者模式定义了一种一对多的依赖关系,允许多个订阅者(观察者)订阅一个发布者(被观察者),并在发布者状态改变时接收通知。与观察者模式不同的是,发布者和订阅者之间没有直接的依赖关系,它们通过消息队列、事件中心或者代理来进行通信。
- 使用订阅发布者模式的情况包括:
当存在多个发布者和订阅者之间的松散关系时,可以使用订阅发布者模式。
当发布者和订阅者之间需要解耦,以便灵活地添加或移除发布者和订阅者时,可以使用订阅发布者模式。
当需要支持多对多的关系,即一个发布者可以有多个订阅者,一个订阅者也可以订阅多个发布者时,可以使用订阅发布者模式。
观察者模式的实现
// 观察者模式
/*** Observer Design Pattern** Intent: Lets you define a subscription mechanism to notify multiple objects* about any events that happen to the object they're observing.** Note that there's a lot of different terms with similar meaning associated* with this pattern. Just remember that the Subject is also called the* Publisher and the Observer is often called the Subscriber and vice versa.* Also the verbs "observe", "listen" or "track" usually mean the same thing.*/#include <iostream>
#include <list>
#include <string>
// 抽象观察者
/*
定义了析构函数,和update函数接口
*/
class IObserver
{
public:virtual ~IObserver(){};virtual void Update(const std::string &message_from_subject) = 0;
};
// 抽象被观察者
/*
定义了析构函数,和attach,detach,notify三个函数接口*/
class ISubject
{
public:virtual ~ISubject(){};virtual void Attach(IObserver *observer) = 0;virtual void Detach(IObserver *observer) = 0;virtual void Notify() = 0;
};// 具体被观察者
/*建议先看main函数,然后再看观察者类,然后再看被观察者类,
不然的话,你就不会知道:为什么被观察者类被称为:"tfboys类"或者是"时代少年团类"或者是"蔡徐坤类"
*/
// 现在咱们来看一下:一个完整的"tfboys"类是如何分装的?
class Subject : public ISubject
{
public:// 重写析构函数virtual ~Subject(){std::cout << "Goodbye, I was the Subject.\n"; // 再见了,我的粉丝们,tfboys于2017年9月正式宣布解散}/*** The subscription management methods.*/void Attach(IObserver *observer) override{list_observer_.push_back(observer); // tfboys将粉丝收集到他的个人列表中,也就是说tfboys会记每一位ta的粉丝!}void Detach(IObserver *observer) override{list_observer_.remove(observer); // 某个粉丝不再对ta进行关注,tfboys就将人家移除个人列表,这个私有列表是用list实现的,是一个双向链表,可以高效的添加与删除每位粉丝,但是查找的复制度是O(N)}void Notify() override // 看到下面的遍历,大家应该猜到了,这是tfboys在举办演唱会之前,对每一位粉丝进行通知,当然这函数不是单独调用的,因为这个函数还有一个message_变量{std::list<IObserver *>::iterator iterator = list_observer_.begin();HowManyObserver();while (iterator != list_observer_.end()){(*iterator)->Update(message_);++iterator;}}// 没错,这个是通知每一位粉丝的真正业务函数void CreateMessage(std::string message = "Empty"){this->message_ = message; // 先把演唱会的相关消息进行编写保存Notify(); // 然后将消息散发给粉丝}// 这是一个debug的函数,用来输出tfboys的私人列表中,到底有几百万粉丝?void HowManyObserver(){std::cout << "There are " << list_observer_.size() << " observers in the list.\n";}/*** Usually, the subscription logic is only a fraction of what a Subject can* really do. Subjects commonly hold some important business logic, that* triggers a notification method whenever something important is about to* happen (or after it).*/// 这个函数其实和CreateMessage一样,只不过这个内置了信息// 不太清楚这个干嘛用的,我可以理解为每次都要发送一些固定的消息,然后我们懒得用CreateMessage编辑了void SomeBusinessLogic(){this->message_ = "Tomorrow will be a holiday!!!";//比如:重要通知:明天放假!!![doge]Notify();std::cout << "I'm about to do some thing important\n";}private:std::list<IObserver *> list_observer_;std::string message_;
};// 具体观察者
class Observer : public IObserver
{
public:// 重写了自己的构造函数,他是用一个被观察者类来初始化,这样一来,被观察者有任何消息,观察者都会收到消息// 就好比很多人都热爱追星: tfboys就是被观察者,他的粉丝就是观察者,所以理论上来说,被观察者干的活应该会更多一点//(所以,为了好记:观察者你可以看成"粉丝类",被观察者你可以看成"tfboys类")// 所以说,下面咱们来看一下,粉丝类是如何封装的Observer(Subject &subject) : subject_(subject) // 这里,我们的粉丝们比较专一,只能选择"时代少年团","tfboys","蔡徐坤"等等,其中的一位明星作为自己的偶像(被观察者){this->subject_.Attach(this); // tfboys的微博收到了订阅请求,恭喜tfboys收获一枚新粉丝!std::cout << "Hi, I'm the Observer \"" << ++Observer::static_number_ << "\".\n"; // 蔡徐坤,你好,我是你的一个新粉丝this->number_ = Observer::static_number_; // 这是记录tfsboys粉丝的总数量,每位粉丝都可以看到 //}virtual ~Observer(){std::cout << "Goodbye, I was the Observer \"" << this->number_ << "\".\n"; // 粉丝被析构了}void Update(const std::string &message_from_subject) override // 这个函数是有tfboys调用的,他要去哪里开演唱会?{message_from_subject_ = message_from_subject; // 将信息保存到粉丝的手机里PrintInfo(); // 通知粉丝,tfboys要去哪里开演唱会了}void RemoveMeFromTheList(){subject_.Detach(this);std::cout << "Observer \"" << number_ << "\" removed from the list.\n"; // gege太令人失望了,一位粉丝默默地离开了ta的应援团}void PrintInfo() // 打印收到了什么新消息{std::cout << "Observer \"" << this->number_ << "\": a new message is available --> " << this->message_from_subject_ << "\n";}private:std::string message_from_subject_;Subject &subject_;static int static_number_;int number_;
};int Observer::static_number_ = 0;void ClientCode()
{// 一个被观察者 subject有"主体"的意思,可以理解为"被观察的主体"Subject *subject = new Subject;// 多个观察者Observer *observer1 = new Observer(*subject); // Hi, I'm the Observer "1".Observer *observer2 = new Observer(*subject); // Hi, I'm the Observer "2".Observer *observer3 = new Observer(*subject); // Hi, I'm the Observer "3".Observer *observer4;Observer *observer5;// subject->SomeBusinessLogic();subject->CreateMessage("Hello World! :D"); // 此时observer1,observer2,observer3都收到了被观察者发送的信息observer3->RemoveMeFromTheList(); // observer3不再观察subject->CreateMessage("The weather is hot today! :p"); // 此时只有observe1,observer2收到了消息observer4 = new Observer(*subject); // observer4加入了观察者observer2->RemoveMeFromTheList(); // observer2不再观察observer5 = new Observer(*subject); // observer5加入观察subject->CreateMessage("My new car is great! ;)"); // 此时只有observe1,observer2,observer5收到了消息observer5->RemoveMeFromTheList(); // observer5不再观察observer4->RemoveMeFromTheList(); // observer4不再观察observer1->RemoveMeFromTheList(); // observer1不再观察delete observer5;delete observer4;delete observer3;delete observer2;delete observer1;delete subject;
}int main()
{ClientCode();return 0;
}
/*
Hi, I'm the Observer "1".
Hi, I'm the Observer "2".
Hi, I'm the Observer "3".
There are 3 observers in the list.
Observer "1": a new message is available --> Hello World! :D
Observer "2": a new message is available --> Hello World! :D
Observer "3": a new message is available --> Hello World! :D
Observer "3" removed from the list.
There are 2 observers in the list.
Observer "1": a new message is available --> The weather is hot today! :p
Observer "2": a new message is available --> The weather is hot today! :p
Hi, I'm the Observer "4".
Observer "2" removed from the list.
Hi, I'm the Observer "5".
There are 3 observers in the list.
Observer "1": a new message is available --> My new car is great! ;)
Observer "4": a new message is available --> My new car is great! ;)
Observer "5": a new message is available --> My new car is great! ;)
Observer "5" removed from the list.
Observer "4" removed from the list.
Observer "1" removed from the list.
Goodbye, I was the Observer "5".
Goodbye, I was the Observer "4".
Goodbye, I was the Observer "3".
Goodbye, I was the Observer "2".
Goodbye, I was the Observer "1".
Goodbye, I was the Subject.
*/
订阅发布者的实现
以下代码并没有用到异步,当然,异步才是正宗的订阅者发布者模式,我暂时是想不到如何改为异步通知,不过,下面的代码也暂时够我们理解 : 在订阅者发布者模式里面的中介是干什么的了
// 订阅者发布者模式
/** 有了上观察者模式的基础,我们先可以尝试编写一下他的升级版--订阅者发布者模式* 在这个模式中,随着tyboys名声大噪,"tfboys",再不需要自己主动通知粉丝了,* 他有了自己的助理,所有大小事宜都交给自己的助理去做*/
#include <iostream>
#include <vector>// 提前声明助理类,不然tfboys不认(其实就是cpp语法,声明前后顺序的问题)
/** 当我们提前声明一个类时,在这个类被完全定义之前,只可以定义该类的变量,但是不能使用该类的方法。*/
class Mediator;// 抽象观察者类
// 抽象粉丝类
class Observer
{
protected:Mediator *_mediator; // 中介者指针public:explicit Observer(Mediator *mediator) : _mediator(mediator){}virtual ~Observer() {}virtual void update() = 0;
};// 具体观察者类
/**具体粉丝类*/
class ConcreteObserver : public Observer
{
public:// 调用基类Observer的构造函数来初始化基类中的mediator_成员变量explicit ConcreteObserver(Mediator *mediator);~ConcreteObserver();void update() override;private:static int static_number_;int number_;
};
int ConcreteObserver::static_number_ = 0;// 抽象发布者类
// 抽象tfboys类
class Publisher
{
protected:Mediator *mediator; // 中介者指针public:explicit Publisher(Mediator *mediator) : mediator(mediator){std::cout << "Hi! I'm a Publisher" << std::endl;}virtual ~Publisher(){std::cout << "Goodbye! I'm a Publisher" << std::endl;}void publish();void setMediator(Mediator *mediator){this->mediator = mediator;}
};// 中介者类
class Mediator
{
private:// 放粉丝的std::vector<Observer *> observers; // 存储观察者指针public:Mediator(){std::cout << "Hi! I'm a Mediator" << std::endl; // 中介的诞生};~Mediator(){std::cout << "Goodbye! I'm a Mediator" << std::endl; // 中介的消亡};// 加粉丝void attach(Observer *observer){observers.push_back(observer);}// 清空某个粉丝void detach(Observer *observer){for (auto it = observers.begin(); it != observers.end(); ++it){if (*it == observer){observers.erase(it);break;}}}// 通知所有粉丝void notify(Publisher *publisher){for (auto observer : observers){observer->update();}}
};/********下面是对两个函数的定义*************/
void ConcreteObserver::update()
{std::cout << "Received an update from the publisher!" << std::endl;
}void Publisher::publish()
{std::cout << "Publishing an update..." << std::endl;mediator->notify(this);
}// 先声明后实现,有效的解决了循环依赖的问题!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
ConcreteObserver::ConcreteObserver(Mediator *mediator) : Observer(mediator)
{this->_mediator->attach(this);std::cout << "Hi! I'm a ConcreteObserver" << ++ConcreteObserver::static_number_ << std::endl;this->number_ = ConcreteObserver::static_number_;
}
ConcreteObserver::~ConcreteObserver()
{std::cout << "Goodbye! I'm a ConcreteObserver" << this->number_ << std::endl;ConcreteObserver::static_number_--;this->_mediator->detach(this);
}int main()
{// tfboys的助理Mediator *mediator = new Mediator(); // 先来一个中介// tfboys绑定一个助理Publisher *publisher = new Publisher(mediator);// tfboys的粉丝Observer *observer1 = new ConcreteObserver(mediator); // 现在粉丝直接和助理进行关联Observer *observer2 = new ConcreteObserver(mediator);Observer *observer3;Observer *observer4;// 下面两行可以搞成异步的,但是我不知道怎么搞// mediator->attach(observer1);// mediator->attach(observer2);publisher->publish();observer3 = new ConcreteObserver(mediator);observer4 = new ConcreteObserver(mediator);publisher->publish();delete observer1;publisher->publish();delete observer2;delete observer3;delete observer4;delete publisher;delete mediator;return 0;
}
/*
Hi! I'm a Mediator
Hi! I'm a Publisher
Hi! I'm a ConcreteObserver1
Hi! I'm a ConcreteObserver2
Publishing an update...
Received an update from the publisher!
Received an update from the publisher!
Hi! I'm a ConcreteObserver3
Hi! I'm a ConcreteObserver4
Publishing an update...
Received an update from the publisher!
Received an update from the publisher!
Received an update from the publisher!
Received an update from the publisher!
Goodbye! I'm a ConcreteObserver1
Publishing an update...
Received an update from the publisher!
Received an update from the publisher!
Received an update from the publisher!
Goodbye! I'm a ConcreteObserver2
Goodbye! I'm a ConcreteObserver3
Goodbye! I'm a ConcreteObserver4
Goodbye! I'm a Publisher
Goodbye! I'm a Mediator
*/
总结
观察者模式中,观察者与被观察者有着密切的联系,耦合度高;
为了解决这个问题,程序员们发明了订阅者发布者模式,在这个模式下,
订阅者与发布者没有直接关系,大大的降低了耦合。
上面是我自己简单的见解,下面来看看高级玩家是如何描述的:
观察者模式(Observer Pattern)是一种行为型设计模式,其中存在一个被观察者(也称为主题或可观察者)和多个观察者之间的关系。当被观察者的状态发生变化时,它会通知所有观察者,并调用相应的方法来处理这些变化。这种模式中,被观察者和观察者之间是直接关联的,因此耦合度较高。
订阅者-发布者模式(Pub-Sub Pattern)是一种消息传递模式,其中存在一个发布者和多个订阅者之间的关系。发布者负责发送消息,而订阅者则订阅感兴趣的消息并进行处理。在这种模式中,发布者和订阅者之间没有直接的关联,它们通过消息队列、事件总线等机制进行通信,从而降低了耦合度。
下面是观察者模式和订阅者-发布者模式的一些关键区别:
1.关系类型:观察者模式是一种对象之间的一对多关系,即一个被观察者可以有多个观察者。而订阅者-发布者模式是一种消息传递机制,发布者和订阅者之间没有直接关联。
2.耦合度:观察者模式中,被观察者需要维护一个观察者列表,并将通知直接发送给观察者。这导致了较高的耦合度。而在订阅者-发布者模式中,发布者不需要知道谁订阅了它的消息,也无需维护订阅者列表,只需将消息发布到消息队列或事件总线中即可。
3.灵活性:由于观察者模式中被观察者和观察者之间的直接联系,可能会导致紧密耦合的设计。而在订阅者-发布者模式中,发布者和订阅者之间解耦,发布者和订阅者的数量和类型可以更加灵活地变化。
总的来说,订阅者-发布者模式通过引入一个中介(如消息队列、事件总线)来降低组件之间的耦合度,使系统更加灵活和可扩展。这种模式在处理大规模分布式系统、异步通信等方面非常有用。然而,在某些情况下,观察者模式仍然是一个简单而有效的解决方案,特别是当只需要维护少量对象之间的关系时。
知识补充:
#include <iostream>using namespace std;class A
{
public:A(){cout << "A" << endl;}private:
};int main(int argc, char **argv)
{A a;//调用构造函数A *a2;//不调用构造函数,仅仅是个指针而已A *a3 = new A();//调用构造函数return 0;
}
/*
A
A
*/