Dynamic dispatch is how conditional logic travels backwards in time
What is dynamic dispatch?
Dynamic dispatch is when your language decides at run time which actual method to call. For example:
On encoutering o.say
, Ruby decides at runtime, based on o’s type,
whether to call Foo#say
or Bar#say
. That’s all dynamic dispatch
is. I remember it being a big deal to C programmers when they first
saw C++, but it’s pretty routine now.
Logging without time travel
Let’s have an object that needs to do some logging, or not.
This is no good because we keep repeating if @verbose
. We have
duplicated how to decide whether or not to log.
This class also has too many concerns. It must do whatever it is that a Foo does, and it must be concerened with how and whether log. We can fix the duplication, and give this class less responsibility.
Logging gets a class of its own (but still no time travel)
Let’s move how and whether to log into its own class:
Foo now takes a log:
And in use:
This is much better. How and whether to log is now in its own class.
But look at these two lines:
A condition that is repeated may be a sign of code that can benefit from polymorphism. Let’s see how.
Applying the Null Object pattern
Let’s apply the null object pattern to the logger and see what happens. We’ll remove all of the conditional code from Log:
and introduce a NullLog which has the same signature, but does nothing:
Then, in use:
The Log class has retained the knowledge of how to log, but it no longer is responsible for knowing whether to log. We’ve given that responsibility to the class that is creating the log instance.
There’s the time travel
Through the use of polymorphism and dynamic dispatch, we have “time-traveled” the decision of whether to log from when the logging is done to earlier in the program’s execution, when the log object was made.
A Factory
There’s another pattern I would usually apply here. The creation of the Log or NullLog can be moved into a factory method:
and in use: