DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
The Latest "Software Integration: The Intersection of APIs, Microservices, and Cloud-Based Systems" Trend Report
Get the report
  1. DZone
  2. Testing, Deployment, and Maintenance
  3. Testing, Tools, and Frameworks
  4. Why Ruby's monkey patching is better than land mines...wait, what?

Why Ruby's monkey patching is better than land mines...wait, what?

Giorgio Sironi user avatar by
Giorgio Sironi
·
Feb. 24, 11 · Interview
Like (0)
Save
Tweet
Share
11.36K Views

Join the DZone community and get the full member experience.

Join For Free

In the last days, the article Why PHP is better than Ruby has got very popular on DZone. Unfortunately, the majority of popular articles are very controversial, and I feel obliged to write a response on one of the so called "pros" of Ruby: monkey patching.

There are other points to address in the article, like the fact that "everything is an object, even literals" premise is really nothing new; I could hit on the fact that getters setters have gone out of fashion even in Java nowadays, but instead I want to focus on monkey patching as it is a dangerous practice (worse than goto) no one ever talks about.

Let's start from the example.

# the class for integers in ruby
class FixNum
def +(adder)
self - adder
end
end

## that's correct biyotches, I just turned addition into subtraction

This code has been chosen as an inherently evil example, I think. Or maybe as a powerful example of the freedom that a Ruby programmers has. However, programming is about constraints and models that produce simplifications, not about freedom.

By the way, PHP has some monkey patching capabilities out of the core, via the runkit extension (but that's for userland classes and functions). But you should never, ever use it.

Operator overloading (+ or - defined for YOUR classes) is actually a good idea, as it consists of well-defined syntactic sugar over method calls. I overload everyday in Matlab for example, to add and subtract histograms objects. When the semantic of the operation is really adherent to the original operator, +, - or *, it's a nice idea to reuse that operator.

But redefining methods on native classes, being them for operator overloading or not, is a bomb waiting to explode.

Singletons

Do you love Singletons and static? Do you unit test your code? Singletons perturb tests since it's usually harder for a unit test to establish a known state. When the code does not call Singletons, the test just has to put together a series of new operator and build a small object graph which will be thrown away after the test itself. When the code contains Singletons, the test must also check that the state of the Singleton is correct, and reset it. However, Singletons are never cited in the Api of the System Under Test, since they are accessed just like global variables instead of being injected.

Monkey patching, like Singletons, is a subtle form of global state, where it's not a bunch of objects hidden somewhere that is modified and alters the results of your test (Singletons), but it's the source code of your classes.

I can't imagine something more evil than this.

A common assumption in object-oriented programming is that native functions and classes are never isolated from your own code via injection. They are just wrapped: nothing is more similar to a list or a string than the real instance. Why this does not cause problems for testability? Because their source code never change (unless you're upgrading your platform, but that's another story.)

Let's suppose we patch SPlObjectStorage::attach(), part of the Standard PHP Library, and add an echo() statement that let us know when an object is added to the collection.

<?php
class AMonkeyPatchedTest extends PHPUnit_Framework_TestCase
{
public function testEchoesElementAdditions()
{
$container = new SplObjectStorage();
$container->attach(new stdClass);
// strange effect: attach echoes something. My What The Frak counter has just increased
}
}

Put yourself in the shoes of your colleague reading and executing this test. How do he finds out the code that is running? There is no clue in the code, it's like if the class was calling a singleton. Except that it is a class provided by the language, so it's strange.

What if we pursue a more testable (and well-designed) solution? A possible refactoring can be this:

<?php
class ACompositionBasedTest extends PHPUnit_Framework_TestCase
{
public function testNotifiesObserversOfElementAdditions()
{
$container = new SplObjectStorage(new EchoElementsAttachingObserver());
$container->attach(new stdClass);
// effect: I have to substitute that EchoElementsAttachingObserver if I do not want anything printed
// a Mock, a NullObject...
}
}

Even if SplObjectStorage is not my class, I have different options:

  • I can subclass it, and override the method (inheritance)
  • I can wrap the native data structure and write my own methods, which contain my functionalities (composition: this option is shown in the test.)

Action at a distance and the Open Closed Principle

When modifying source code on the fly, the all-powerful Action at a distance anti-pattern arises:

Action at a distance is an anti-pattern (a recognized common error) in which behavior in one part of a program varies wildly based on difficult or impossible to identify operations in another part of the program. The way to avoid the problems associated with action at a distance are a proper design which avoids global variables and alters data in a controlled and local manner. -- Wikipedia

With monkey patching, you can effectively break other code simply by adding new classes (particularly, by adding new source code which opens up already known classes, being them native or in userland code). This contradicts the Open/Closed Principle:

In object-oriented programming, the open/closed principle states "software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification"; that is, such an entity can allow its behaviour to be modified without altering its source code. This is especially valuable in a production environment, where changes to source code may necessitate code reviews, unit tests, and other such procedures to qualify it for use in a product: code obeying the principle doesn't change when it is extended, and therefore needs no such effort.

What happens when you release a monkey patch to a production environment? A golden fail whale?

Incompatibilities

You can say that you're responsible on what you monkey patch. That you only touch method A on class B, and it is not vital, and that your modification is backward compatible. Then two libraries, and you, override the same method and the application explodes again.

System administrators usually freeze version of libraries on servers in order to avoid modifying the global state of the server: you'll never want to upgrade from PHP 5.2 to PHP 5.3 automatically (or even upgrade at all, if the old application is working well), for fear of compatibilities breaks. Now why would I want change a native class source code on the fly? To get everything else (in my application) that calls that class in peril?

Conclusion

Monkey patching is a practice which involves substituting the pillars of an house: if you're not very careful in what you substitute, the whole building will collapse over your remains. Moreover, you may take down some underground stations full of people as well as a side-effect.

Before insulting a language because it's not possible to monkey patch in it, think about the costs and benefits of adding such a feature. One of the tenets object-oriented programming is message passing between objects as a mechanism of decoupling different parts of the program: there is nothing "decoupled" in changing a class definition which impacts many objects in disparate parts of the object graph.

unit test

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • When Should We Move to Microservices?
  • OpenVPN With Radius and Multi-Factor Authentication
  • Choosing the Right Framework for Your Project
  • Java Concurrency: LockSupport

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: