I was reading a blog post by Derick Bailey last night and I got into the comments due to a few tweets that I saw (I’m on Twitter if you want to follow me). It was a lot of noise and ranting, but one topic that came up was that people working in languages such as C# and Java quite often mistake classes for objects. Working in a language like C# they can feel quite similar, since the distinction is merely that a class represents the abstraction of a set of objects, while an object is just an instance of a class. You can think of the class as the cookie cutter and the object is the cookie.
So what is the importance of this distinction? Well, in C# prior to 4 the distinction wasn’t very important. You didn’t really have a good way of interacting with a class in a way that the class definition didn’t provide. In C# 4 the dynamic type was introduced along with a late binding mechanism that allows the object to respond to methods which don’t exist on the class definition. So, are they really method calls at all? We aren’t actually calling a method, right? This is exactly why many languages will talk about message passing rather than calling methods.
In a language such as Ruby, I can pass a message to an object, and that object decides whether or not to respond to the message. Saying that I am calling a method directly implies that the object has no say in what occurring. Assuming that object has that method, then it gets called. In C# most of these calls are bound at compile time, so a method call is generally a tightly woven contract. If you ever hear someone say that C# is class oriented while Ruby is object oriented, then this is why. C# has implemented object oriented programming using classes, while Ruby has many more mechanisms for performing object oriented development.
In Ruby, since passing a message is not tightly bound, the object just receives the message and then decides what to do with it. It might have a method that was defined on the class which satisfies the message. If that is the case then the method will be executed. The object might also have a method which was added to it via another mechanism such as a mixin or calling define_method. Or, there might not be a method at all, the object might respond to the message by checking it internally and deciding what to do. In Ruby this behavior is implemented using the method_missing method.
I hope now you are starting to see the important distinction between classes and objects. In a language like C# we take a class declaration and punch out instances of our classes using them. Once an object has been created, it is for most practical purposes, set in stone. We can’t really add methods or change its signature. We can do things like put interfaces on our classes in order to allow us to switch out one instance of a object for an instance of a different object, but we can’t simply change the signature of the class at runtime.
And this, in a nutshell, is why Ruby doesn’t have or need interfaces. An interface, as we define it in C# is a contract which specifies inputs and outputs on class. The inputs and outputs of an object in Ruby are much more fluid, and aren’t decided at compile time (since Ruby doesn’t compile in the traditional sense). Since the interface of any given object is simply what messages it is willing to respond to, and that can change over time, it wouldn’t make a lot of sense to try and put a contract on that. There is no guarantee an object will respond to a message, and there is no compile time step in order to assert that contract.
So what does all of this mean? Well, if you are working with C# then it probably doesn’t mean very much unless you are using the dynamic type and implementing the DynamicObject class. The DynamicObject class is what will fairly easily allow you to change the interface of an object at runtime. If you are working in Ruby then you are probably already intimately familiar with these concepts. In Ruby, classes are only a small part of the OO picture. And in fact, classes are objects themselves, and they can be interacted with as objects. Objects that are responsible for creating other objects. Bet that just blew your mind!
In the end, I hope that this encourages you to take a look at Ruby and get outside your comfort zone of what you think an object oriented language is and should be. Experiencing the freedom of Ruby may make you giddy with glee, or it may make you run screaming back into C#’s arms and be immensely thankful that you have compile time type checking. Personally, I think that there are advantages and disadvantages to both, and everyone should experience both to see what they prefer.
Loved the article? Hated it? Didn’t even read it?
We’d love to hear from you.
Excellent post Justin. I often point out the differences between class oriented and object oriented by showing how much time I spend in my code interacting with actual objects versus telling the compiler what each type is and what it can do via interfaces and classes. In true object oriented languages, I feel you spend less time talking to the compiler and more time talking to your objects (and getting stuff done).
Usually this comes up when people tell me that JavaScript isn’t an object oriented language. They make that mistake because they don’t see the traditional class based constructs like getters/setters, interface and class declarations, or type annotations. Heck JavaScript, Ruby, and Python are nothing BUT objects.
@Scott Thanks! To your point, I think that the recent distinction of "class" oriented languages is a useful one. While distinguishing between class and object oriented languages can lead to confusion, I think pointing out that languages like C# and Java implement their OO-ness with classes is important to developers realizing that there is a world out there that doesn’t rely solely on classes. Pointing out JavaScript and its prototype based inheritance model is a great example.
Yeah when I first started using ruby and python duck typing blew my mind. Then I started working with python and realized an object was essentially a dictionary of functions and fields. Then I learned that a method was just a function with an implicit passing of the ‘self’ dictionary. And finally I found decorators and the heaven and earths opened to the wonders of AOP.