На тангенциальной линии рассуждений вы можете использовать для этого существующие библиотеки, например boost::signal
, которые позволяют вам определять событие и подключать слушателей (наблюдателей) к этому событию.
// Forgive the lack of encapsulation and const-correctness to keep the example simple:
struct Animal {
boost::signal< void ( Animal& )> signal_update;
std::string name;
};
class Bird : public Animal {
public:
void rename( std::string const & n ) {
name = n;
signal_update(*this);
}
};
class Zoo
{
public:
Zoo() : bird() {
bird.signal_update.connect( boost::bind( &Zoo::an_animal_changed, this, _1 ) );
}
void an_animal_changed( Animal & a ) {
std::cout << "New name is " << a.name << std::endl;
}
Bird bird;
};
int main() {
Zoo zoo;
zoo.bird.rename( "Tweety" ); // New name is Tweety
}
Преимущество (и недостаток) этого решения заключается в том, что оно ослабляет связь между наблюдателем и субъектом. Это означает, что вы не можете обеспечить, чтобы только Zoo
s могли наблюдать за животными или чтобы метод, используемый для наблюдения , имел конкретную подпись / имя. В то же время это преимущество, если ваш Animal
не знает или не заботится о том, кто наблюдает:
class Scientist {
public:
Scientist( Zoo & zoo )
{
zoo.bird.signal_update.connect( boost::bind( &Scientist::study, this, _1 ) );
}
void study( Animal & a ) {
std::cout << "Interesting specimen this " << a.name << std::endl;
}
};
int main() {
Zoo zoo;
Scientist pete(zoo);
zoo.bird.rename( "Tweety" ); // New name is: Tweety
// Interesting specimen this Tweety
}
Обратите внимание, что как тип, так и имя функции могут быть адаптированы с помощью boost::bind
. Если ученый работает в двух зоопарках, он даже может быть уведомлен о том, к какому зоопарку принадлежит животное, которому принадлежит смена:
// [skipped]: added a name to the zoo
void Scientist::work_at( Zoo & zoo ) {
zoo.bird.signal_update.connect( boost::bind( &Scientist::study, this, _1, zoo.name ) );
}
// updated signature:
void Scientist::study( Animal & a, std::string const & zoo_name )
{
std::cout << "Interesting animal " << a.name << " in zoo " << zoo_name << std::endl;
}