Sometimes when you are doing rapid development, you need to create a new method right now, but you don’t put the method in the right spot. Maybe you aren’t sure what object should ultimately have the functionality. Maybe you are simply too lazy to open the correct file. Whatever the reason, you decide to define the method right there at the top-level, pretending you are a procedural programmer.
Time passes, and you finally decide it is time to move that method to where it belongs. The problem is that someone (certainly not you…) has been calling that method from everywhere. Fixing all the calls to the method at once will be a huge change; you prefer to make smaller, easier to review changes. Or maybe you don’t have control over all code that calls the method, and so you can’t change all the the calls.
Here’s an example of the problem
1 2 3 4 5 6 7 8 9 10 11 12 |
|
You’d really like MyObject
to have an add_value
method, instead of
it just floating around at the top-level. However, you don’t want to
just copy code around – what if one of the implementations changes?
Your first instinct might be to do something like this:
1 2 3 4 5 |
|
The problem with this is that once you are inside the class
definition, all calls to add_value
will be handled by the instance,
not the top-level method.
You could avoid this by making the top-level method and the instance method have unique names. If you like the existing name for both methods, you could alias the original name to a more unique one to avoid shadowing it:
1 2 3 4 5 6 7 |
|
Another solution is to qualify access to the top-level method. This can be done by maintaining a reference to the object that defines the method.
1 2 3 4 5 6 |
|
- We start by stashing a reference to the object that defines the top-level method. This object is named “main”.
- If we reopened our class the traditional way, using
class MyObject
, the reference to main would fall out of scope. Instead, we useclass_eval
. - The definition of the
add_value
instance method must usesend
as the top-level method is private.
If you don’t want to modify the object directly, you can apply the same techniques to a module:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Using modules leads to another solution. You can move the top-level method into a module, and use the module with both the class and the main object.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
The downside to this solution is that the object now has two
add_value
methods. One of the methods takes any instance of the
class, which would be confusing to anyone trying to figure out how to
use the object.
The easiest and clearest solution to this problem is not to use clever metaprogramming, but just flip the way you think about the problem. Move the entire method into the object and leave a stub method that redirects to the object’s implementation:
1 2 3 4 5 6 7 8 9 |
|