I'm trying to get around the ugly conditional casting involved when interpreting an update() call for an observable that will want to notify its observers of multiple types of events. Also, I'd prefer not to pass flags to the notifyObservers()/update() method.
I do not want the observers to have to poll the observable object to find out what's changed, 开发者_开发知识库I'd like this new data to be given to the observers via the update() method (or similar.)
I have an idea for a solution. I instanciate one observable object for each type of notification. For example: The observable is an object representing a hardware device, it will contain observables representing its state:
public final Observable connectionState = new Observable();
public final Observable dataState = new Observable ();
This way, observers don't need to do any sort of querying or conditional casting, as one observer, i.e. one overridden update() method can be used per notification type.
After much frustration this is the most elegant solution I can think of, however I have this horrible feeling that I've missed the point about how to use Observers/Observables properly.
My main issues with that solution are:
- It still involves a cast (at least it's not conditional)
- Since the observables need to be observable, they must be public members. While this does allow observers to call addObservable(), it also allows them to call notifyObservers().
Am I doing the right thing?
Thanks
You are struggling with the limitations of the Java 1.0 implementation of the Observer pattern. Take a look at this answer to the question Is java.util.Observable used anywhere?
They are not used, because their design is flawed: they are not type safe. You can attach any object that implements Observer to any Observable, which can result in subtle bugs down the line.
Wrapping them inside a type safe interface is about the same amount of work as implementing the pattern from scratch, so I guess the latter is preferred in most cases.
Rather than trying to shoehorn your requirements into java.util.Observer, maybe you should just implement your own version of the pattern that better fits your needs.
You can try to use Observable paired together with Visitor pattern:
class MyObserver implements Observer, EventVisitor {
public void update(Observable o, Object arg) {
((EventAcceptor) arg).accept(this);
}
public void visit(SomeEvent v) {
System.out.println("SomeEvent: " + v.s);
}
public void visit(AnotherEvent v) {
System.out.println("AnotherEvent: " + v.info);
}
}
interface EventVisitor {
void visit(SomeEvent v);
void visit(AnotherEvent v);
}
interface EventAcceptor {
void accept(EventVisitor v);
}
class SomeEvent implements EventAcceptor {
public final String s;
public SomeEvent(String s) {
this.s = s;
}
public void accept(EventVisitor v) {
v.visit(this);
}
}
class AnotherEvent implements EventAcceptor {
public final String info;
public AnotherEvent(String info) {
this.info = info;
}
public void accept(EventVisitor v) {
v.visit(this);
}
}
class MyObservable extends Observable {
void handleSomeEvent() {
setChanged();
notifyObservers(new SomeEvent("some event"));
}
void handleAnotherEvent() {
setChanged();
notifyObservers(new AnotherEvent("some event"));
}
}
class Sample {
public static void main(String[] args) {
MyObservable observable = new MyObservable();
observable.addObserver(new MyObserver());
observable.handleSomeEvent();
observable.handleAnotherEvent();
}
}
There are lots of possible solutions for your problem. If you feel the Observable class isn't suited to your problem you can maintain your own collection of listeners.
interface Listener {
void onEvent1(Type1 arg);
void onEvent2(Type2 t2, Type3 t3);
void onEvent3();
}
List<Listener> listeners = new CopyOnWriteArray<Listener>();
public void addListener(Listener l) { listeners.add(l); }
public void onEvent1(Type1 arg) {
for(Listener l: listeners) l.onEvent1(arg);
}
精彩评论