4 ms·
Agree that instanceof is wrong and discredits the article. I'm not sure if this is what you meant, but here is how polymorphism along with the double dispatch
by sivanmz 12y ago
Agree that instanceof is wrong and discredits the article.
I'm not sure if this is what you meant, but here is how polymorphism along with the double dispatch pattern would work: NaiveActor#handleLifecycleMessage will need to delegate to the event subclass, which will in turn select the appropriate method on NaiveActor.
Example of subclass of LifecycleMessage:
class ExitMessage implements LifecycleMessage {
@Override
void handle(BasicActor actor) {
actor.handleExitMessage(this);
}
}
In NaiveActor:
@Override
protected void handleLifecycleMessage(LifecycleMessage m) {
m.handle(this);
}
@Override
protected void handleExitMessage(ExitMessage m) {
if (Objects.equals(m.getActor(), myBadActor) {
System.out.println("My bad actor has just died of '" + m.getCause() + "'. Restarting.");
spawnBadActor();
}
return super.handleExitMessage(m);
}
It's been my experience that many developers resort to instanceof or enum type flags because they don't believe polymorphism actually works in real world situations such as this.
- mbrock 12y agoI think a lot of programmers aren't totally clear about how dispatch works in Java. I've talked to many who couldn't explain exactly why it doesn't work to overload based on subtype information only known at runtime. Type flags are extremely obvious!
- spopejoy 12y agoThere's something wrong with this approach, though: it couples ExitMessage and BasicActor, in that BasicActor would have to have a method for each event type, resulting in lots of empty methods, etc. What I'm describing is a marker interface for eventing, with specific subtype APIs: interface LifecycleListener { } interface StartListener extends LifecycleListener { void onStart(Foo f); } interface ExitListener extends LifecycleListener { void onEnd(Foo f); } along with a single registration method (perhaps a LifecycleObservable API, or in a class also offering event-firing methods): Disposable addLifecycleListener(LifecycleListener l) The benefits to the client code are numerous: clarity (dedicated methods for events), declarative code ('implements' section documents interactions), performance (no dispatching in client code). [Note, Disposable here represents a dispose() function to deregister the listener, avoiding the classic pair of void register/deregister methods. Often I find void methods represent a missed opportunity ... wishing Java was more like Smalltalk here] The service side has to jump through some hoops to efficiently dispatch, using class equality or instanceof at registration time to pre-select listeners. This code could certainly use pattern-matching but personally I think it would look identical. If anything, I'm glad that pattern-matching isn't available in this case, to at least alert framework devs to the problem and not push it off to tons of switching on the client.