Flaws of Object Oriented Modeling

At the beginning of the computer era the system designers came from the world of hardware and it is noticeable. In hardware there are many working elements that can operate in parallel and most times at different rates of operation. This requires hi degree of accuracy in system timing. Chip designers count the number of transistors between two elements to make sure that the Operation Flow is maintained.

The Assembly language defines a set of primitive / native operations. The programming process and software design using Assembly is in direct correspondence with the execution flow. If you want the flow to break then you use the Jump operation explicitly. This is because Assembly was originally designed by hardware developers and was written to accommodate the hardware.

The C language is a procedural language. It was originally created by Assembly programmers (as 'B'). We can still see Assembly type of thinking operations built in the language, for example:
++ is INC
-- is DEC
[A] ? [B] : [C] is equal to:
[A] ;// do operation [A]
JZ ;// if true go to [B]
[C] ;// else do [C]
JMP ;// go to end
[B]
When you are used to working with Assembly you get used to thinking in "test" – "do this if so" – "do this if not". C programmers hardly ever use this.
The C language is a procedural language. It helps us group together sets of operations and also releases us from the need to use Jumps or Go To-s, which can be very simple to track execution flow if you use it correctly but can be easily abused into what was coined as "Spaghetti Code" because of all the lines you need to draw when you try to track the execution flow of an application that was designed incorrectly.

Next evolution, came the language of C++ which is Object Oriented in design. This allows the separation of code modules into discrete software units called a class. Object Oriented programming allows multiple teams of developers to work on the same project very easily. Object Oriented languages can really help the developer manage the code.

The problems that came with Object Oriented programming is that these languages are really designed to help the developer manage the code…
Now it is almost impossible to follow the execution flow. When you want to know what happened in your application when the error message popped up you have to go over all the objects and all the classes that the execution flow went through and many times execution flow travels between several member functions before leaving to another object. Following the execution flow today is almost impossible.

Many times I have seen 'Pure' Object Oriented design producing a code that is a collection of many three-line functions that call each other. Every button click in the application travels through ten or more small functions. You can no longer follow the execution flow by reading the code. This brings two major problems that we face today.

The first problem is that it is no longer possible to detect execution flow bugs with a simple code review. Going over an Assembly code it is very easy to detect simple bugs such as down-casting, potential overflows etc. Reading an object oriented code you can't see the big picture and it is often impossible to review all the small functions that call the one function that you modified.

The second problem with this model is the "Not my fault" syndrome. "I only called a member of another object and it returned FALSE. Don’t ask me why". This is how you get an error that says "Problem with saving the document. Reason is '0x8000745 – unknown', What would you like to do?" What do YOU think I should do?! The programmer got this return value from some object that he is not familiar with and has no idea what this value means so he just pushes that to the higher level. The last level that you can propagate to is the user, and so my mother keeps facing these interesting decisions when she is trying to save a picture.

Object Oriented Modeling was invented to help developers manage the code but had no regard for execution flow. Up till now we used to use step-by-step debugging to see the execution flow. This is no longer relevant when we plan on having multiple threads going over our code. You single step one thread and another completes 5000 loops in the background. If you have multiple threads going over the same function they might all stop on the same breakpoint and you have no way of telling which is which effectively.

Following execution flow today is a terrible problem.

This is the first in a collection of articles that will introduce a new model called the Operation View Model with motivation for using it. This model can define any element in the computer world and it is the next step in software evolution.
Next article will demonstrate how operating systems follow an evolution pattern that is similar to the ones described here.

Comments (28)

You&#39;re not wrong... for overly simplistic understanding of object-oriented programming you present here. In reality object-oriented programming is a very broad subject with many interpretations: there are boundless approaches towards every aspect of object-oriented programming!

The problems you touched on here also exist in procedural programming with functional decomposition and are not inherently problems with object-oriented programming.

Some approaches to object-oriented programming have very good concurrency semantics; some use powerful dispatch mechanisms and don’t force the programmer to use conditional expressions in place of polymorphism; some of don’t force programmers to use a flawed inheritances mechanism! The fact that mainstream object-oriented programming languages do it wrong doesn’t mean that object-oriented programming itself is flawed.

Basically all elements of the code can be browsed to their actual definition and for each element of code (routines or classes) you can also figure out who is using it as well as what it is using. With this kind of information on hand it is very easy to grasp a large system. For more info, check http://www.eiffel.com and http://docs.eiffel.com

Yanic, in my post I was saying that "And if you have the right tool then following code execution is really not a problem." So indeed Eiffel as a language does not bring you this, but EiffelStudio code browsing facilities let you grasp code path execution very quickly. No other tool on the market for C++, Java or C# is able to do better than EiffelStudio.

The whole idea of OO is to compartimentalize behaviour into coherent units (i.e. concept oriented), and as a side effect the behaviour for a single 'use case' is spread over many different classes. A different approach would be to group behaviour according to use cases (i.e. task oriented), but that has other problems since tasks are less stable than concepts in a problem domain and behaviour needs to be reused across use cases.

I believe however that the problem lies not with OO approaches but dismal tool support.

And btw, Eiffel does nothing to solve the problem of control flow passing through many objects to achieve a single task.

I think that you used the wrong O-O language and tools. Eiffel which was created more than 20 years ago does not have the flows mentioned above. Why? Simply because Eiffel is a language and a method for building large, reliable and reusable software. The key aspect of Eiffel is `Design By Contract': a software system is viewed as a set of communicating components whose interaction is based on precisely defined specifications of the mutual obligations -- contracts (as Business people do with Business contracts.) More details at http://www.eiffel.com/developers/design_by_contract.html

And if you have the right tool then following code execution is really not a problem. An Eiffel programmer can usually manage between 500K and 1M by himself.

In any case I'm curious to read more about your Operation View Model and how we could apply it.

Sure objects are great for more than just managing code - it's the perfect approach for merging algorithms with data; not to mention the benefits of polymorphism. However, if you take a good look at C++, you'll see that it's more than just an OO language. A true multi-paradigm C++ program combines preprocessor macros, functional decomposition, task oriented programming, generic programming, etc and of course OO.

In order to develop truly robust software, you need to use all the tools at hand. If all you use is an OO-hammer, everything is going to look like a nail-object.

As I was reading this I was saying to myself "He's right, you know." I can barely stand to read OO programs. I invariablely end up jumping back and forth from file to file in order to trace the logic. If I didn't have Visual Studio or some other IDE to point out where things are defined, I'd give up completely. I can see a more operartion or task-centric model would make things easier to parallelize.

(Of course, I only masquerade as a C++ programmer in seedy wharf-side bars, so take my opinions with a teaspoonful of salt.)