This post is part of an ongoing series about Affordances and Programming Languages.
Many programming languages provide support for reflection, which allows for very creative metaprogramming when solving problems. The reflection facilities in various languages differ in the kinds of features they provide, their ease of use, and their verbosity.
Reflection is a kind of affordance.
Reflection is often used to implement the Factory Pattern. As an example, consider an application that receives commands over a socket. Each command takes a different set of arguments and performs some more or less complex action in response. This example was inspired by John Pignata’s excellent talk at Mountain West Ruby Conference 2013, Code Smells: Your Refactoring Cheat Codes.
Assuming that each command has been encapsulated into its own subclass
Command, the obvious solution is to dispatch commands with a case
Note that C++ doesn’t allow strings as case options in a
statement, so we’re forced to use a series of
instead. This is also a kind of affordance.
Every time we add a new command, we have to update this
statement. Since C++ only has minimal reflection capabilities, we
might choose to stop there, or we might introduce some kind of command
registry using macros or explicit initialization.
In Ruby, since we have reflection and metaprogramming facilities, we can come up with something a little more maintainable:
Now, adding a new command is as simple as adding one more entry in the
In Ruby, superclasses don’t know about their subclasses by default. This is not true of Smalltalk, though, so we can take advantage of that:
By taking advantage of Smalltalk’s reflection, we can completely
eliminate any central registry of commands. Now, adding a new command
is as simple as introducing a new Command subclass. As long as the
new class inherits from
Command and implements the class-side
token method, it will automatically be found by the dispatcher.
It is possible to implement a similar approach in Ruby using
Class#inherited, but that’s a bit more work to set up.
And that’s actually the point of affordances. Because Smalltalk provides direct access to subclasses, it affords the ability to find the commands dynamically. Because Ruby and Smalltalk provide reflection, they afford a cleaner implementation of the factory pattern than languages without reflection.