views:

1502

answers:

8

I'm slowly moving from PHP5 to Python on some personal projects, and I'm currently loving the experience. Before choosing to go down the Python route I looked at Ruby. What I did notice from the ruby community was that monkey-patching was both common and highly-regarded. I also came across a lot of horror stories regarding the trials of debugging ruby s/w because someone included a relatively harmless library to do a little job but which patched some heavily used core object without telling anyone.

I chose Python for (among other reasons) its cleaner syntax and the fact that it could do everything Ruby can. Python is making OO click much better than PHP ever has, and I'm reading more and more on OO principles to enhance this better understanding.

This evening I've been reading about Robert Martin's SOLID principles:

  • Single responsibility principle,
  • Open/closed principle,
  • Liskov substitution principle,
  • Interface segregation principle, and
  • Dependency inversion principle

I'm currently up to O: SOFTWARE ENTITIES (CLASSES, MODULES, FUNCTIONS, ETC.) SHOULD BE OPEN FOR EXTENSION, BUT CLOSED FOR MODIFICATION.

My head's in a spin over the conflict between ensuring consistency in OO design and the whole monkey-patching thing. I understand that its possible to do monkey-patching in Python. I also understand that being "pythonic" is to follow common, well-tested, oop best-practices & principles.

What I'd like to know is the community's opinion on the two opposing subjects; how they interoperate, when its best to use one over the other, whether the monkey-patching should be done at all... hopefully you can provide a resolution to the matter for me.

+4  A: 

In my view, monkeypatching is useful to have but something that can be abused. People tend to discover it and feel like it should be used in every situation, where perhaps a mixin or other construct may be more appropriate.

I don't think it's something that you should outlaw, it's just something that the Ruby guys like to use. You can do similar things with Python but the community has taken the stance that things should be simpler and more obvious.

BrianLy
+3  A: 
Kent Fredric
Frankly, I disagree. The ability to add functionality to where it logically belongs makes very much sense to me. foo.delay(5000, args); is significantly more readable to me than a regular setTimeout. It is precisely the kind of monkeypatching that makes code MORE READABLE rather than unintuitive and surprising like bad monkeypatching can do. It is powerful, and possible to abuse, but it can help make code more understandable too. For the record, I don't use mootools, but that's a cool use of monkeypatching.
Mike Stone
A: 

Monkey-patching is just plain wrong, IMHO. I've not come across the open/closed principle you mention before, but it's a principle I've long held myself, I agree with it 100%. I think of monkey-patching as a code-smell on a larger scale, a coding-philosophy-smell, as it were.

jTresidder
Definitely a philosophy smell, nicely put.
JoeBloggs
+1  A: 

My first thought is that monkey-patching violates OCP, since clients of a class should be able to expect that class to work consistently.

quamrana
+2  A: 

You may find enlightening this discussion about Ruby's open classes and the Open-Closed Principle.

Even though I like Ruby, I feel monkey-patching is a tool of last resort to get things done. All things being equal, I prefer using traditional OO techniques with a sprinkle of functional programming goodness.

Alan
+3  A: 

Mokeypatching is generally wrong. Create a proper subclass and add the methods.

I've used monkeypatching once in production code.

The issue is that REST uses GET, POST, PUT and DELETE. But the Django test client only offers GET and POST. I've monkeypatched methods for PUT (like POST) and DELETE (like GET).

Because of the tight binding between Django client and the Django test driver, it seemed easiest to monkeypatch it to support full REST testing.

S.Lott
+20  A: 

There's a difference between monkey-patching (overwriting or modifying pre-existing methods) and simple addition of new methods. I think the latter is perfectly fine, and the former should be looked at suspiciously, but I'm still in favour of keeping it.

I've encountered quite a few those problems where a third party extension monkeypatches the core libraries and breaks things, and they really do suck. Unfortunately, they all invariably seem stem from the the third party extension developers taking the path of least resistance, rather than thinking about how to actually build their solutions properly.
This sucks, but it's no more the fault of monkey patching than it's the fault of knife makers that people sometimes cut themselves.

The only times I've ever seen legitimate need for monkey patching is to work around bugs in third party or core libraries. For this alone, it's priceless, and I really would be disappointed if they removed the ability to do it.

Timeline of a bug in a C# program we had:

  1. Read strange bug reports and trace problem to a minor bug in a CLR library.
  2. Invest days coming up with a workaround involving catching exceptions in strange places and lots of hacks which compromises the code a lot
  3. Spend days extricating hacky workaround when Microsoft release a service pack

Timeline of a bug in a rails program we had:

  1. Read strange bug reports and trace problem to a minor bug in a ruby standard library
  2. Spend 15 minutes performing minor monkey-patch to remove bug from ruby library, and place guards around it to trip if it's run on the wrong version of ruby.
  3. Carry on with normal coding.
  4. Simply delete monkeypatch later when next version of ruby is released.

The bugfixing process looks similar, except with monkeypatching, it's a 15 minute solution, and a 5-second 'extraction' whereas without it, pain and suffering ensues.

PS: The following example is "technically" monkeypatching, but is it "morally" monkeypatching? I'm not changing any behaviour - this is more or less just doing AOP in ruby...

class SomeClass
  alias original_dostuff dostuff
  def dostuff
    # extra stuff, eg logging, opening a transaction, etc
    original_dostuff
  end
end
Orion Edwards
There is definitely a time and place for monkey patching, but I do agree that it can and will be abused. Another kind of monkey patching is the Antivirus products and malware rootkits - they move stuff around in the kernel - which causes all kinds of hurt.
Redbeard 0x0A
I think this sums up your post well: "With great power comes great responsibility." Frankly, I love monkeypatching, but I tend to avoid overriding existing methods (though I will consider it if it really is the best option), for exactly the reasons you illustrate.
Mike Stone
+1  A: 

In my eyes, monkey-patching is one form of AOP. The article Aspect-Oriented Design Principles: Lessons from Object-Oriented Design (PDF) gives some ideas of how SOLID and other OOP principles can be applied to AOP.

Esko Luontola