OOP is broken

12.09.2013 Permalink

For me, a nice side-effect of learning a functional language like Clojure is that I started asking questions about things that I considered as 'given' for a long time. (Yes, it's my fault that it took such a long time.)

One personal conclusion I'm now very confident of is that OOP is fundamentally broken for at least three different reasons.

1) OOP conflates state with identity. You can see symptoms of this problem everytime you start thinking about thread-safety, synchronization and locking. Concurrency in programs is a fact, it occurs on the server side and in more hideous ways on rich clients. Our systems have to deal with it.
OOP models the world naively: the person 'Mike' and all the data that describes 'Mike' are essentially the same (a.k.a 'object'). But the correct approach is: 'Mike' as person is an identity that we give to a chronology of discrete perceptions that we get from Mike. Each perception is immutable, it's a fact, only valid at some point in time. Developers must overcome this OO deficiency all the time by confining mutable state to threads or making objects immutable as much as possible or -- worst case -- resort to synchronization and locking. This severely limits our ability to catch up with the increasing demand for even more concurrency in the future.

2) When using OOP we tend to create islands of functionality and data called 'classes' that are -- at best -- composable for a small number of foreseeable cases. OOP does not keep its promise about reuse at small scale. In addition, the highly praised encapsulation of implementation details is broken all too often by recklessly added accessors.
To compose OO class functionality developers often need to apply patterns like Adapter combined with a Factory. To move data around in a system developers use all sorts of mapping, often in disguise of a 'binding framework' (see JSF ManagedBeans, JavaFX Binding, JAXB or Dozer), or -- more obvious -- as 'OR-Mapping'. The consequence is an explosion of code whose only purpose is to bridge the waters between the islands.

3) There is this valuable Open-Closed-Principle in OOP. To add behaviour to a class without modifying its original implementation a developer can either: The first three options usually require that the developer has influence on how instances are created. If this is given then a common next step is to employ the Factory pattern on top.
The fourth option doesn't feel like idiomatic OO so many developers are reluctant to go this way. As a fix languages like C# or Xtend offer syntactic sugar called Extension Methods, which are superfluous in a 'verb first' functional language. In the end the OO approach for extensibility is -- at best -- cumbersome. The result is usually an increase of incidental complexity and maintenance cost.

The three flaws I listed above are fundamentally baked into OOP. This is the reason why I don't bet on OO languages like Java, Ruby, Scala, Xtend or Groovy anymore. They may provide workarounds, but no cure. In order to gain competitive advantage I recommend to learn Clojure. Your minimum benefit is that you become conscious about the flaws of the OO language you have to use for a living. Thus, it will make you a better programmer. Now, ask yourself: do you want to become better?