So the recent debate on… well it’s not actually clear on precisely what… is continuing. That’s good, after all we do need to actually think about what we do as programmers. Discussion is wonderful thing. But I do think it is important to try to be clear on what we are discussing.
It is a Data Orientated Design Vs. Object Orientated Design debate? Or is it attack/defend on the concept of abstraction? And what on earth am I saying in this debate anyway?
It seems clear looking at various reactions to my previous post that not everyone is necessarily getting what I am saying. For example, I am absolutely not saying that code is more important than data, although reading one blogged response from the headline you would think that is what I am saying. Additionally, I am absolutely not saying that Data Orientated Design is wrong. It is a way of looking at code and has it’s uses, especially when interfacing to hardware where data organisation has a massive effect on performance.
I am actually not a ‘fan’ of Object Orientated Design either, believe it or not. I use it, and the OOD facilities in C++ are important and useful for certain programming design problems. However increasingly as I have developed my own personal take on the art of programming I have found that something is a bit wrong.
I will defend, however, the concept of abstraction and this is the primary focus of my previous post. This may be an argument over semantics: what I mean by abstraction may not be what others mean. What I mean by abstraction is something close to Dijkstra’s view of it:
Argument four has to do with the way in which the amount of intellectual effort needed to design a program depends on the program length. It has been suggested that there is some kind of law of nature telling us that the amount of intellectual effort needed grows with the square of program length. But, thank goodness, no one has been able to prove this law. And this is because it need not be true. We all know that the only mental tool by means of which a very finite piece of reasoning can cover a myriad cases is called “abstraction”; as a result the effective exploitation of his powers of abstraction must be regarded as one of the most vital activities of a competent programmer. In this connection it might be worth-while to point out that the purpose of abstracting is not to be vague, but to create a new semantic level in which one can be absolutely precise. Of course I have tried to find a fundamental cause that would prevent our abstraction mechanisms from being sufficiently effective. But no matter how hard I tried, I did not find such a cause. As a result I tend to the assumption —up till now not disproved by experience— that by suitable application of our powers of abstraction, the intellectual effort needed to conceive or to understand a program need not grow more than proportional to program length. But a by-product of these investigations may be of much greater practical significance, and is, in fact, the basis of my fourth argument. The by-product was the identification of a number of patterns of abstraction that play a vital role in the whole process of composing programs. Enough is now known about these patterns of abstraction that you could devote a lecture to about each of them. What the familiarity and conscious knowledge of these patterns of abstraction imply dawned upon me when I realized that, had they been common knowledge fifteen years ago, the step from BNF to syntax-directed compilers, for instance, could have taken a few minutes instead of a few years. Therefore I present our recent knowledge of vital abstraction patterns as the fourth argument.
– From Dijkstra’s “The Humble Programmer” 1972
To put it in simple terms, abstraction is the only method known for managing complexity. Managing complexity is important because computer programs can become incredibly complex – they are probably the most complex machines mankind has ever created. Without abstraction, large computer projects are impossible to realize. And the first key abstraction of computer programming came with the invention of the subroutine.
So what about the connection between abstraction and OOD? The truth is that abstraction is independent of OOD, and in some ways (brace yourselves) OOD is actually counter to the idea of abstraction. The reason I say this is because the key idea of abstraction in programming is to hide implementation details from the users of interfaces. As an example, let’s take moving an object in a game scene. Somewhere I can create a function call that repositions an game object. When I do this I am not interested in the internal workings of the rendering engine, so the interface is actually all about the concept of placing an object. Data does not enter into it at all, and in fact any data exposed by the interface class is a break down of the abstraction. Even if made private, such data is annoyingly present in the class.
To get around this, we can create abstract interfaces, that contain no state. State is actually an implementation detail. In fact, accessor functions are really an abstraction where an interface is provided for some functional activity, but the data is hidden. This kind of abstraction is good news because it means that my code for placing an object does not care which data structures are involved under the hood, meaning that the programmers on that side of things are free to change the data formats to optimise their engine as much as they want without upsetting the higher level code (such as the game logic).
This, however, highlights a contradiction. The whole point of Object Orientated Design is to group data and functionality together. And the conclusion I have come to is (brace yourselves again) that grouping data and functionality together is very often the last thing you want to do. In other words, object orientated programming does not work as a cure all for programming problems, precisely because it wants to keep data and functionality together, when the aim of abstraction is more commonly to keep data and functionality separate!
Aha! Now I think things may be falling into place.
DOD and OOD are not enough. We also need FOD (functionally orientated design). There is a new fangled thing called AOD (aspect orientated design), but having looked at it I am going to stick with FOD for the moment. AOD is certainly connected with my FOD, but seems more concerned with being a kind of band aid on OOD to solve some of its problems.
What do I mean by FOD? Well, it means design orientated around functionality; design focused on functionality and not concerned with data (which is an implementation concern). For example, in a game we could state “When I press this button the character should jump”. Such a statement is a definition of functionality, but has no data content at all. Under the hood, a lot of data gets pushed around, transformed, created an destroyed to make that happen. But the functional abstraction hides all that… and good thing too for the game player!
A heuristic I use when building architectures in C++ comes from this, and other things too: I try to make my header files as clean as possible, and try to keep state (that is data) out of them. I have found this an exceptionally good heuristic for keeping C++ under control and to do this I have to use many of the “Object Orientated” features of C++, but the irony is that by doing so I am actually not really embracing object orientated design at all, because I am trying to keep data and functionality separate.
So the end result, I think, is something like this:
FOD <-> GLUE <-> DOD
Now the GLUE is interesting, because more or less it is where DATA meets FUNCTION. This is actually where OOD can come in useful, because it naturally is concerned with the fusion of the two. However, you really do not need to use OOD to make the GLUE at all. There are many ways to join function to data, and this I think brings me to the end of my deliberations here:
I am struck with the thought that Object Orientated Design may well have been a huge distraction in the development of the art of programming. In its efforts to help organise code, it may have actually hindered the development of most important abstraction of all, that between function and data. One should not dismiss OOD; it remains a useful concept, but I do not think it is deserving of the pride of place it has been given.
When designing software, we do not start in the middle, with the glue: we start with the functionality and the data and work inwards. And we should choose whatever software design methodologies we deem appropriate in between. But the one thing that is key throughout the whole process is the principle of abstraction.