问题
I'm trying to use the Observer pattern for some input stuff like so:
class Observer
{
public:
virtual void notify(Subject & o)=0;
};
class Subject
{
public:
virtual void register(Observer * o)=0;
}
I have two concrete Subjects (Mouse, Keyboard), with class specific functions that I want the concrete observer to call( getkeypress, getmousemotion etc).
Is there any way to specialise the notify function in the concrete observer class without changing the interface, or downcasting the reference? I've tried overloading the function, but obviously that doesn't work because the concrete Subjects have no knowledge of derived Observers.
回答1:
You usually don't give the observer a pure virtual notify function. Instead, your Subjects should reimplemented a "changed" function that Observer::notify calls on all its Subjects. This, you can reimplement in both Mouse and Keyboard to call the functions you want.
This does require changes to your interface as presented, because right now it's not quite right.
回答2:
Is there any way to specialise the notify function in the concrete observer class without changing the interface, or downcasting the reference?
I don't think there is.
However, you can minimize the number of places you use dynamic_cast
by using a class template that is a sub-class of Observer
and making the type used to instantiate the class template to be fully aware of the derived type.
I have used that pattern effectively numerous times.
Solution 1
This uses a pattern where an observer can observer only one type of object.
#include <iostream>
class Subject;
class Observer
{
public:
virtual void notify(Subject & o)=0;
};
class Subject
{
public:
// Remove the Observer argument from the public interface.
// Make the derived class construct the right type of observer
// and use the registerObserverImpl function to do the work.
// With this, client code doesn't need to know the kind of Observer
// a sub-class of Subject uses.
virtual void registerObserver() = 0;
void notifyObserver()
{
observer->notify(*this);
}
protected:
// Helper function for derived classes.
void registerObserverImpl(Observer * o)
{
observer = o;
}
private:
// The observer.
Observer* observer;
};
// A class template that is responsible for performing dynamic_cast
// and passing a reference to the derived type to the concrete Observer.
template <typename RealObserver>
class TemplateObserver : public Observer
{
using ConcreteSubject = typename RealObserver::SubjectType;
virtual void notify(Subject& o)
{
// The only place you need to use dynamic_cast.
RealObserver().notify(dynamic_cast<ConcreteSubject&>(o));
}
};
class Mouse : public Subject
{
public:
virtual void registerObserver();
};
// The concrete Observer of Mouse. It doesn't need to be derived from
// Observer since TemplateObserver takes care of that.
class MouseObserver
{
public:
using SubjectType = Mouse;
void notify(Mouse& m)
{
std::cout << "In MouseObserver::notify\n";
// Use the Mouse anyway you want.
}
};
void Mouse::registerObserver()
{
registerObserverImpl(new TemplateObserver<MouseObserver>());
}
class Keyboard : public Subject
{
public:
virtual void registerObserver();
};
// The concrete Observer of Keyboard. It doesn't need to be derived from
// Observer since TemplateObserver takes care of that.
class KeyboardObserver
{
public:
using SubjectType = Keyboard;
void notify(Keyboard& k)
{
std::cout << "In KeyboardObserver::notify\n";
// Use the Keyboard anyway you want.
}
};
void Keyboard::registerObserver()
{
registerObserverImpl(new TemplateObserver<KeyboardObserver>());
}
int main()
{
// Client code does not need to know about MouseObserver or
// KeyboardObserver.
Mouse m;
m.registerObserver();
m.notifyObserver();
Keyboard k;
k.registerObserver();
k.notifyObserver();
}
Output
In MouseObserver::notify
In KeyboardObserver::notify
Solution 2
This uses a pattern where an observer can observer any numbers of types of objects.
#include <iostream>
class Subject;
class Observer
{
public:
virtual void notify(Subject & o)=0;
};
class Subject
{
public:
// Make the class polymorphic
virtual ~Subject() {}
void registerObserver(Observer * o)
{
observer = o;
}
void notifyObserver()
{
observer->notify(*this);
}
private:
// The observer.
Observer* observer;
};
// A class template that is responsible for performing dynamic_cast
// and passing a reference to the derived type to the concrete Observer.
template <typename RealObserver, typename RealSubject>
class TemplateObserver : public Observer
{
virtual void notify(Subject& o)
{
// The only place you need to use dynamic_cast.
RealObserver().notify(dynamic_cast<RealSubject&>(o));
}
};
class Mouse : public Subject
{
};
// The concrete Observer of Mouse. It doesn't need to be derived from
// Observer since TemplateObserver takes care of that.
class MouseObserver
{
public:
using SubjectType = Mouse;
void notify(Mouse& m)
{
std::cout << "In MouseObserver::notify\n";
// Use the Mouse anyway you want.
}
};
class Keyboard : public Subject
{
};
// The concrete Observer of Keyboard. It doesn't need to be derived from
// Observer since TemplateObserver takes care of that.
class KeyboardObserver
{
public:
using SubjectType = Keyboard;
void notify(Keyboard& k)
{
std::cout << "In KeyboardObserver::notify\n";
// Use the Keyboard anyway you want.
}
};
class CombinedObserver
{
public:
void notify(Mouse& m)
{
std::cout << "In CombinedObserver::notify\n";
// Use the Mouse anyway you want.
}
void notify(Keyboard& k)
{
std::cout << "In CombinedObserver::notify\n";
// Use the Keyboard anyway you want.
}
};
int main()
{
// Client code does not need to know about MouseObserver or
// KeyboardObserver.
Mouse m;
m.registerObserver(new TemplateObserver<MouseObserver, Mouse>());
m.notifyObserver();
Keyboard k;
k.registerObserver(new TemplateObserver<KeyboardObserver, Keyboard>());
k.notifyObserver();
m.registerObserver(new TemplateObserver<CombinedObserver, Mouse>());
m.notifyObserver();
k.registerObserver(new TemplateObserver<CombinedObserver, Keyboard>());
k.notifyObserver();
}
Output
In MouseObserver::notify
In KeyboardObserver::notify
In CombinedObserver::notify
In CombinedObserver::notify
来源:https://stackoverflow.com/questions/43038525/observer-pattern-specialisation