Huw: Back in June 2005, you wrote a column in which you argued that Object Oriented Programming (OOP) has turned out to be a failure. In the period since then, you and I have been working on the Ruby In Steel IDE written for one object oriented language (Ruby) and written (largely) in another object oriented language (C#). Is this an admission that you got OOP all wrong when you criticised it before?
Dermot: A little background first. I’ve been working on a Visual Studio ‘package’ which adds Ruby editing and debugging support to Visual Studio 2005. Visual Studio, in its current incarnation anyway, is based on COM interfaces. COM is object oriented in a way, but isn’t object oriented in the modern sense in that it doesn’t use inheritance. I’ve implemented the Ruby part using a Microsoft-provided tool – the fully object-oriented Managed Package Framework or MPF - and the debugger part by connecting up C# code to the COM interfaces. So I’ve used both object orientation with inheritance (the MPF) and the older and simpler COM ‘encapsulation’ extensively.
In working with the MPF, the main difficulty has been the sheer complexity of figuring out what was going on (the thing they don’t tell you about writing packages for Visual Studio is that is damned hard work!). It wasn’t made any easier by having to work through a class hierarchy. At the end of the process, I haven’t derived a lot of things from the original MPF, but instead, I copied it and modified it (extensively) to make it do what I wanted. I thought long and hard before doing this – but it was the right decision.
But further than that, I ended up duplicating a lot of what I had already copied because I needed two ‘languages’ – Ruby and ‘embedded Ruby’ in Rails template files. I did manage to leverage some of the supposed benefits of OO in that I didn’t have to duplicate everything, but that was offset by having to track down some deeply subtle bugs in the MPF hierarchy which were mainly due to me using the wrong version of an object – the non sub-classed version rather than the derived version for example.
In contrast, the COM debugger implementation was more straightforward. There was no MPF to help – but equally, the MPF wasn’t in the way either. Now, of course, there were other problems as anyone who has tried to connect C# code up to a COM system will tell you (I spent a week tracking down memory leaks), but on the whole, I far preferred doing the COM stuff to hacking my way through derived types and classes.
So to sum up, my experience over the last year has confirmed what I’ve observed and experienced over many years – Object Orientation is vastly overrated and overused. The MPF isn’t bad – I suspect it’s fairly typical of such things, but I honestly feel that it would have been quicker to have been presented with a simple COM implementation ‘template’ and been left to modify as required. In the end, I think this would have been the faster route.
This has been reinforced by observing the difficulties others have had in using the MPF. All you have to do is look at some of the posts in Microsoft’s Visual Studio Extension forum to see what I mean.
Huw: I said just now that both Ruby and C# are object oriented. In spite of that, they are very different languages. In fact, most ‘mainstream’ OOP languages such as C#, Delphi, Java and C++ take a pretty laid back (or maybe that should be ‘sloppy’?) approach to OOP. C++ and Delphi are a mishmash of procedural and OOP; Java and C# are better but still seem to have cherry-picked bits of procedural languages and mixed them up with a few OOP ideas.
For example, one of the key ideas of object orientation is encapsulation. But most OOP languages implement just one bit of encapsulation – by binding methods into classes – and forget the other bit: information hiding. In C#, for example, it’s entirely up to the programmer whether or not the variables inside an object are accessible to code ‘on the outside’.
I can’t figure out why so many of the people who design OOP languages don’t seem to think information hiding is important. If other programmers can poke around inside your objects, your encapsulation is hopelessly broken. Some people say, well, heck, you can make variables private. But with a language like C#, you can never be certain that other in a programming team people aren’t writing code that depends on the implementation details of classes. One of the great ideals of OOP is supposed to be that each object is self-contained; its implementation details are hidden and the only way to access data is via well-defined interfaces – data goes into a method in one place and is returned at another place. But when data hiding is not enforced, you can never be sure that this will be the case. That’s a big loophole, isn’t it?
Dermot: I’ve never been that bothered about data hiding. It’s a nice to have but when you are at the sharp end of trying to figure out what’s going on its not that important.
Huw: Really? But how about when you are working with large teams? I know you’ve worked on some big projects in the past – international banking systems and whatnot. Surely it would have been enormously useful to have the sure and certain knowledge that the stuff you intend to be private – the implementation details of all your coding – really does remain private.
In my view, it is simply neater, safer and more damned elegant when programs are divided up into neat chunks with clearly defined routes of communication. Whether that’s done via modularity as in Modula-2 or encapsulation as in Smalltalk, doesn’t really bother me. In my opinion, data hiding is one of the great ideas of programming to which many people have paid lip service but which no mainstream language takes seriously or does thoroughly. That goes for Ruby too. There are all kinds of odd ways in which you can poke about inside an object or retrieve a value from a method which the person who wrote that method might never have intended you to use. As far as I’m concerned, if you are going to do encapsulation, you should do it properly or not at all.
Dermot: That’s all true, but it comes back to building interfaces to do the data hiding. Of all the projects I’ve worked on, the best was a client-server dealing room system which was based around a set of interfaces that I specified. There was no way that the individual programmers could see behind these interfaces and so they didn’t fall over one another. Not only was the data hidden, the internal wiring was as well. From time to time, I had to re-specify the interfaces, but because the interface was the ‘terminal’, so to speak, you didn’t have the OO problem of the changes propagating down the OO hierarchy like a demented hacker on drugs. This was some time before COM was invented by the way.
Huw: So what do you think are the main benefits of OOP? Encapsulation? Inheritance, polymorphism? Something else…?
Dermot: Ha! The main merit seems to be that ‘gurus’ can invent an ‘ology’ and stick their names to the front of it! I’ve come across some semantic drivel in my time, but OO books are by far the worst. More seriously, I think that OO works well for ‘frameworks’ like the .NET Framework and it’s also not bad when you need to make small changes to existing programs. But most programs simply don’t fall into those categories. I would guess that most programming activity goes into databases of one form or another and the changes to these are not driven by anything approaching OO methodologies. So you have the fundamental problem that the user (often the government) has done a 180 degree somersault leaving your precious OO model high and dry. So what do you do? Rewrite your OO model to reflect the fact that you’re going to the North Pole and not the South? Or bastardize your OO design? Most designers will do the latter (quite simply because the user will not accept that a re-design is required) – with the usual consequences.
Huw: Of all the fundamental OOP ideas, I have to say that the one that’s never really persuaded me is inheritance. True, it has its place. Sometimes it can be very useful (in an adventure game I wrote, it was, indeed, invaluable – though I’m not sure how ‘typical’ of OOP projects an adventure game really is). In some cases, I’d say inheritance causes more problems than it solves. I remember wasting a good morning’s work writing some features for Ruby In Steel this summer only to discover subsequently that the same features had already been implemented in one of Microsoft’s base classes. I didn’t know about that because the base class definition was deep inside another file in another directory. It was only when I went back and hunted around in that code that I realised that my code was repeating something from its parent class. I’m sure this must be a common problem. Have you fallen into any traps like that?
Dermot: Actually, I don’t think that’s an OO problem. Rather, it’s a problem of intrinsic complexity. But where OO makes it worse, is that people think that they are making things simpler for the end-user by burying the logic somewhere. In fact, they are just digging a deeper hole.
Huw: I have to say that it seems to me that an awful lot of supposedly ‘OOP’ code these days is still written in a traditional – ‘procedural’ – style. By that I mean that classes often tend to take the place of what you would call a code library in a procedural language. They are used to wrap up vast amounts of loosely related code for easy reuse. Methods in C#, say, or even in Ruby, are frequently quite long but the class hierarchy is relatively shallow. Compare that with the way that Smalltalk classes are written. In Smalltalk, methods are typically very short but the class hierarchy is very dense. I guess you could write classes like that in C# or Ruby but the development environments don’t really support that style of coding. In Smalltalk, the code and its environment work together very tightly so when one class descends from another, you can instantly see this relationship in the hierarchy browser. Maybe that’s one of the things that modern OOP languages forgot – the importance of making the language work in cahoots with its environment.
Dermot: I’d agree with you here. I don’t think OO works very well away from an IDE with a first class browser. In fact, if you look at what the designers of Smalltalk built – it was conceived as an integrated whole. The ‘browser’ was just as important as the ‘Smalltalk’ language, the ‘mouse’, the ‘virtual machine’, the ‘windows’ … come to think of it, was there anything that those guys didn’t invent?
Huw: Be honest with me: if you were given a free choice, would you prefer to be working in an OOP language or a procedural one?
Dermot: Well, I wouldn’t like to go back to C++. That’s a nightmare! But I’ve been struck by how well C# works with COM in Visual Studio (much against my initial expectations, actually). The COM interfaces define a cast iron ‘surface’ against which you can program with certainty. Clearly, there are problems – memory management being number 1 and finding out what on earth an arbitrary COM pointer actually implements (I’ll give you 100 guesses) being number 2. But what I’m going to do in Ruby In Steel is rework my major class definitions into interfaces and use the class to implement the interface. That seems to be about right: it’s solid without being too clever.
Also, I wouldn’t want to loose managed memory, but I do like the simplicity of C. I find programming Microchip microcontrollers in C and assembler positively relaxing! But in all honesty the language that I’ve come across that satisfies a) strong interfaces, b) memory management and c) a simple C like syntax isn’t Ruby … it’s D.
There you have it…
OOPS! or: Where Did Object Orientation Go Wrong…?
Ruby – Hidden Treasure or Flawed Gem?
The D Programming Language: interview with Walter Bright
D – C Done Right…?
Programming Milestones: Smalltalk
S# - Smalltalk: The Next Generation – interview with David Simmons