Well, for better or worse, I've been doing a lot of Ruby coding lately.
One of the first great selling points that Ruby developers like to flaunt is that their language is 'truly object oriented' in that everything is an object, even numeric literals. Really? Cool! Well, I've done a lot of object oriented programming over the last twenty years... You know, languages like Java, C++, Objective-C, ObjectPascal, and yes, even a little SmallTalk.
With the exception of SmallTalk, all of these languages feature visibility modifiers that allow you to identify a method as public, protected, or private, and all of the languages present consistent behavior when you tag your methods as such. In particular, private methods allow you to write code that is specific to the internal implementation details of your class, and those methods are never exposed outside of your class.
Ruby has private methods too... or does it? Take this code for example:
class BaseClass
private
def private_method
puts "base class private"
end
public
def public_method
private_method
end
end
class SubClass < BaseClass
private
def private_method
puts "sub class private"
end
end
d = SubClass.new
d.public_method
According to the traditional rules of object-oriented inheritance, what should the last line of this snippet display? If you were thinking "base class private," you'd be thinking like an object oriented programmer who has spent more than three minutes writing code in Java or C++, but according to Ruby, you'd be completely fucking wrong.
Why? Well, Ruby, while it doesn't allow you to call private methods from outside of their classes, does allow you to override them in subclasses. That's okay, other OOP languages allow you to do that as well, except for one thing: other OOP languages don't add private methods to the class's virtual method table, and so there's no risk involved in defining methods of the same name in descendants.
Ruby makes it even worse by making private methods accessible to subclasses. That's what
protected methods are for! So what used to be the proper way of hiding implementation details from consumers of your APIs has now become a fucking minefield. The Ruby developer stance regarding this mess is "well, you should be familiar with the code you're extending, or you should just write an adapter."
I'm sorry, I thought the value of object-oriented programming was to allow developers to utilize or inherit the public functionality of APIs, while remaining buffered from the internal details of those APIs. It was supposed to save us time and allow us to concentrate only on the functionality that we needed to implement rather than the inner workings of functionality that we're leveraging.
As Ruby developers, I suppose if I decided to open a socket and send a byte buffer from point A to point B, you'd also recommend that I learn the internal workings of the Linux kernel? In that case, I should probably also know the inner workings of the BSD kernel as well, because it also has a socket() call.
Ok, so let's say I follow your advice and I refer to the code that I'm extending and purposely avoid the definition of methods that duplicate the names of private methods in a base class. What happens if the upstream vendor of an API that I'm using decides to add a private method to their base class that duplicates the name of a method in my class? Should they keep tabs on the thousands of potential 3rd party extensions to their classes? Should we be forced to perform code reviews every time we update a dependent API?
What the fuck, Ruby people?! I shouldn't be seeing "sub class private." Fix this!
Technorati Tags: C++, Java, Objective-C, Programming, Ruby, SmallTalk