Objects are data, functions, behaviors, contracts, everything. If you came from the plain-old-C age, you would be familiar with a much simpler way of structuring your code: structures as records of data fields, and functions as collections of transformation steps that affect these data structures.
The procedural approach to programming is more strictly structured than OOP.
OOP was born out of procedural programming, as an extension. That extension, called classes, did not narrow down the possibilities or put additional constraints. It opened up a rather complex world of possibilites, by allowing a free unrestricted mix of data and functions called an “object”. One common rant agains OOP is that “OOP forces everything to be an object”. Joe Armstrong, designer of the Erlang functional Language, expressed this rant very strongly . I think the truth is quite the opposite. It’s not that OOP forces everything into an object, it’s that an object can be everything in OOP, and as such it’s hard to say what the object construct is meant for. I would rather second objections along the lines of Jeff Atwood’s entry in that OOP is not a free ride.
A class can be a data structure, and in this case the encapsulation traits are probably not extremely interesting and the class itself could well be a structure. A class can be a collection of methods only, without shared state. In this case it’s like a C module. A class can be a contract, when it contains virtual functions. A class can be the implementation of a contract with hidden and encapsulated state. A class can be many more things.
I think that one of the productivity issues with OOP, at least the C++ way (and all other derivatives) is that all these different use cases are syntactically represented in the same way, as a class. The class construct is totally devoid of any specialization, and as such it’s both extremely powerful and hard to handle. The software architect needs to specialize the class into a meaningful tool for the problem at hand. OOP in this sense is a meta-programming paradigm, which does require some thoughtful selection of language features and how these should be bent to the goals of product creation. This becomes even more evident if you look into all the “companion” language features of C++, like templates, multiple inheritance or friend classes. If you choose OOP, you have to define rules of how to use the language, much more so than in the procedural language case. Java and C# made some moderate attempts at specializing the class construct by adding the interface keyword. It might be interesting to see what an even more constrained OOP language could look like. A language with special syntax for data classes, behavior classes, user interface classes, and so on. A language that naturally leads the developer to choose a nearly optimal tool for the job. Any language designer out there? For the time being, architects are called to do this step in frameworks instead of languages.
So, if OOP requires so much planning and choice of the tools, why has it become so popular? In my mind, it’s because of two reasons. First, because flexible structuring allows software designers to create libraries and frameworks with the reuse patterns they have in mind and they need. As Spiderman said, with great power comes great responsibility, and that’s what OOP gives and demands.
The second, maybe the most important reason, is that the object way of decomposing a problem is one of the most natural ways of handling complexity. When you plan your daily work activities, are you concerned about the innards of the car you are driving to reach the office? Do you need to know how combustion in the engine works? Do you need to check out the little transistors in your CPU to see they are all twitting correctly? Me not. I rely on those things working as expected. I don’t need to know details of their internal state. I appreciate that somebody encapsulated and hid their variables and workings in convenient packages for me to consume. It’s like this with objects, and it’s like this with human organizations. We all regularly delegate important work to others and trust them, maybe after signing some contract, that they will provide us with the results we need. Delegation and work-by-contract is what defines human structures as well as OOP, which is why OOP is popular for large software architectures.
There’s maybe one last perspective. Object orientation might favour static structures over processes made of steps, or state machines where state keeps changing. By hiding the changing state, OOP could give the impression of a perfect world of static relationships. The word “perfect” comes in fact from the latin composition of per-factum, that is complete, finished, done. If it’s done it does not change anymore and it’s thus static. Clearly a static structure is easier to observe than something which keeps changing, so perfection of static structures is more in the eyes of the beholder who can then appreciate all details. Science, for instance, is about capturing what changes in formulas that do not change and thus can be used for predictions. It’s not just an observer perspective, as static and long lasting structures are more worthy of investigation than brief temporary situations.
To sum it up, the bias of OOP towards static structures is natural and useful in describing large architectures.