Monkey Patching for Mere Mortals



Sometimes you're in the middle of development and you need a custom method that is not included in the default classes that are provided. A typical example is you need a String.left method that returns everything to the left of a particular index, but oops... IntelliSense pops up and there is no String.left method sad. Sometimes writing a new class is great, but this messes with flow, because you create a separate class to store your "utility" method then 4 months you forget you even created it. Wouldn't it be fun to extend the classes dynamically? Well most dynamic languages offer this functionality.

In C# this is called extension methods, in JavaScript prototypes ... Some languages make this very easy and I'll demonstrate how to create this in several languages.
/// <summary>
/// The following function returns all characters left of a index value len
/// </summary>
/// <remarks>
/// <param name="s">String to be manipulated</param>
/// <param name="len">index value</param>
/// <returns></returns>
public static string Left(string s, int len)
{
    return s.Substring(0, len);
}

/// <summary>
/// The following function returns all characters left of a index value len
/// </summary>
/// <remarks>
/// <param name="this">String object</param>
/// <param name="len">index value</param>
/// <returns></returns>
public static string Left(this string s, int len)
{
    return s.Substring(0, len);
}

Calling the 1st example would be :
string result = Left(“hello world”,5);
The 2nd
“hello world”.left(5); //The string would be the object (this) passed in as the first param

C# 3.0 allows us to create extensions methods.  With an extension method, we "extend" the String to add the missing function. We create an extension method by first placing the method inside a static class, making the method static and using the this keyword with the object type as the first param (this string s)


JavaScript example


/**
 * The following function returns all characters left of a index value len
 * @param len - index val / length
 */
String.prototype.left = function(len) {
    return s.substring(0,len);
}

NB: The string class in java is final so “extending class” is not as easy as calling the extend keyword. We could use java reflection to manipulate this value at runtime, but this can be a little cryptic and it is not for the “faint of heart”. For this example I’ve extended the String class in groovy using the metaclass, very nifty class that can be used to dynamically modify a class and its methods at runtime. An example is shown below

/**
* Groovy example
* The following function returns all characters left of a index value len
* @param len : length to be truncated
*/
String.metaClass.left = { len ->
    return s.Substring(0, len);
}


Although monkey patching is a great feature be careful when you use it. It’s a very powerful feature and it used incorrectly can manipulate the behavior of your code drastically. Imagine the following scenarios

/**
* Groovy example
* The following function returns all characters left of a index value len
* @param len : length to be truncated
*/
User.metaClass.increasePay = { amt ->
    return this.user == “ferron” ? this.pay * amt : this.pay + amt
}


In the above example a hacker wrote a script and hid it some arbitrary class and obfuscate the code so that is. The normal flow of the application expects the increasePay method to be in the domain, but little did anyone know that a hacker had a grand idea. This code could be a lot more colorful, but I’m just demonstrating a simple case where this can be dangerous.


Alvid Grim notes : “
Monkey patching is the new black [in the Ruby community]. It's what all the hip kids are doing. To the point that smart, experienced hackers reach for a monkey patch as their tool of first resort, even when a simpler, more traditional solution is possible.
I don't believe this situation to be sustainable. Where I work, we are already seeing subtle, difficult-to-debug problems crop up as the result of monkey patching in plugins. Patches interact in unpredictable, combinatoric ways. And by their nature, bugs caused by monkey patches are more difficult to track down than those introduced by more traditional classes and methods. As just one example: on one project, it was a known caveat that we could not rely on class inheritable attributes as provided by ActiveSupport. No one knew why. Every Model we wrote had to use awkward workarounds. Eventually we tracked it down in a plugin that generated admin consoles. It was overwriting Class.inherited(). It took us months to find this out.
This is just going to get worse if we don't do something about it. And the "something" is going to have to be a cultural shift, not a technical fix. I believe it is time for experienced Ruby programmers to wean ourselves off of monkey patching, and start demonstrating more robust techniques. “
Alvid points out that the user of monkey patching is:
  • Needless - another technique (such as a mixin, or locally extending individual objects) would have worked as well or better.
  • Overcomplicated - the use of a monkey patch actually created more work for the author.
  • Fragile - the solution is tightly bound to third-party internals, reducing the usefulness of the plugin or gem because it is prone to breakage.
  • Excessively wide in scope - by hardcoding extensions to core classes, the author takes the choice to scope the change out of the plugin/gem user's hands, further limiting utility.


This is not to alienate folks from the wonders of monkey-patching, indeed it is fun, but remember to use it sparingly, because it can have serious implications if it is not. Sun made the classes final for a reason :) 

Comments

Popular posts from this blog

JavaScript Module Pattern: 2 Forms

Pseudo-Random UUID Generation with mask support

Mocking Ajax with the JQuery Mockjax Library