When someone like Brian Goetz is giving a talk, it’s almost always worth listening. His talks at the 2018 JAX in Mainz, Germany, were no exception. There was an idea resonating particularly well with me, so let me paraphrase this part of Brian’s talk. Or rather, what I made of it: I didn’t take a recording, so adding my own thoughts and misconceptions is inevitable.
But they said object-orientation is the way to go!
Now, that’s throwing out the baby with the bath water. I understand why people react this way, but it’s not the right answer. But it reminds of another observation I made one or two decades ago when I was deep into object-oriented programming. I believed that object-oriented programming is the only sensible approach, basically because I was taught so. Later it dawned on me that many popular frameworks optimized for business applications don’t support object-orientation well. That’s not a fault. It’s a voluntary, even sensible decision. I don’t like it, but that’s another story.
I first noticed this surprising trait with SAP’s WebDynpro framework. When I learned about WebDynpro in 2010, I didn’t like the framework because it was ill prepared for object-oriented programming. There were objects, but there was nothing like inheritance. I wasn’t able to draw sophisticated UML diagrams of my applications written with the Web Dynpro framework. As far as I remember, even the objects were incomplete. You could insert your code in predefined methods, you could add methods, but that’s all you could do.
At the time, I was both a successful software architect and an astronaut architect. I believed in the textbooks. They told me that inheritance was the way to go. WebDynpro didn’t support that, so WebDynpro was crap. It’s just that easy.
Business applications don’t need object-oriented modeling
Later I started discussing with my co-workers. Many of them used procedural languages such as Natural. Some of them even used Assembly language. Obviously, they couldn’t program in an object-oriented way. But the interesting observation was that they didn’t miss it. It dawned on me that my arrogance was a bit off. Knowing the reality from the textbooks is not the same as knowing reality.
It took some time, but after I while I started to admit the truth: object-oriented programming is great for many things. It’s a great tool for everyone creating a library. In particular, it’s useful to create a UI component library. Objects are the tool of choice for reusable stuff, be it components or libraries.
But if you’re programming business applications, you hardly ever benefit from inheritance in particular and object-oriented programming in particular. Objects are still a useful metaphor, simplifying many discussions, but you don’t really need them. They are expendable. The one single feature that makes OO useful in business applications is that objects naturally form a namespace. You can categorize your objects, group them in packages or folders, and so on. It’s what book libraries have been successfully doing for ages. If someone tells you they’re exclusively doing functional programming, I’m sure they still group their functions in a similar way.
A fresh, new look at object-orientation
So let’s have a look at the good parts of object-orientation. Objects allow you to compose an application from smaller parts. They are a divide-and-conquer strategy. In other words: objects are great for defining (and identifying) boundaries between components, modules and so on. From this point of view, the modules introduced with Java 9 are just another variety of objects: they have an implementation and an API. In fact, modules have been added to the language because developers needed a tool to define large-scale units with a clearly-defined boundary. Objects match this metaphor only for fairly small units.
Brian compared this with the apartment you’re living in. There’s an API (the entrance). In modern times, this door is usually a security door. You keep it locked all the time. Only authorized people get in.
Encapsulation is a great tool to protect the boundaries of the object. Objects have public attributes and methods (the API) and private ones (the implementation). What’s a bit weird about the Java world is that everybody believes in using getters and setters. We’re taught to make every attribute private and to explicitly expose only the API attributes using a getter and a setter.
Actually, I can’t see why using a setter is better than simply making the attribute public. Granted, setters and getters allow you to modify the implementation later, but be honest: how many times have you done so? The last time I remember is more than 15 years ago. We pile up a lot of boiler-plate code to prepare for a situation that never happens.
Funny thing is that this idea never became popular for methods. There’s the façade pattern, but it’s only used when it’s really needed (unless an astronaut architect like my younger me is on your team).
Security doors in your flat?
When I learned Java, nobody used getters and setters. They have been invented for a completely different use case. In 2000 (or so), the idea was to provide visual components for Swing applications. People loved their visual UI editors, so the challenge was to provide a visual component you can simple drag from a component palette and drop it into the application window. Getters and setters were part of the component framework that made this possible. As far as I know, this idea never took off. Instead, developers discovered the idea, considered it useful and propagated the dogma to encapsulate everything with getters and setters.
The idea isn’t bad, but it was used in an exaggerated way. Nowadays, developers decorate every object with getters and setter. Popular frameworks like Hibernate enforce this programming style. That’s like installing a security door between your dining room and your bathroom. Only authorized visitors are allowed to enter the bathroom. However, in real life, everybody sitting in your dining room can safely be considered an authorized person. Granted, there are thieves, but your principal interest is to keep them out of the apartment, not to keep them out of a particular room.
So maybe we should start thinking before we encapsulate an object. It makes a lot of sense to encapsulate a component or a module. That’s typically a larger unit consisting of several objects. You don’t need encapsulation within the component because most components are small enough “to fit in your head”. You need all these powerful tools when you’re reaching the limits of your brain capacity. Plus, most internal objects aren’t exposed, anyway. You’re only exposing the API.
Different rules within a component and between components
Cutting a long story short, object-oriented programming a great for defining boundaries between units of your application. It’s a lot less useful for implementing the unit itself (at least if it’s a small unit). Inside the unit, functional programming is much more useful.
Functions have many useful traits. They don’t have side effects. That’s one of the reasons why Lambda expressions in Java can only use effectively final attributes of the surrounding scope. That’s also the reason why I proposed to always use static methods in an earlier article.
You can easily write a unit test for a function without side-effects. Multi-threading is also easy as long as you don’t have any state. In other words, functions are a great tool to unleash the power of your multi-core CPU.
Like objects, functions are also a divide-and-conquer tool. You can compose a complex algorithm from simple functions. The difference to objects is that functions are about behavior. They model the dynamic aspects of your application.
Are functional programming and object-oriented programming antagonists?
After all these thoughts, I wonder why so many developers perceive the two programming styles as mutually exclusive. They aren’t. The natural choice is to use objects for the large-scale units of your program, such as modules, components, and so on. The realm of functional programming is manipulating data, which usually has a much more local scope.
Maybe – just maybe – many developers think different because they are programming business applications using a third-party framework. The major part of most business specifications describes how things are done. In other words, they describe behavior and data manipulation.
If you’re using a framework like React.js, that allows you to implement a component as a simple function, you might even get away without ever jotting down the “class” keyword. Conceptionally, a functional component in React is still sort-of a class. It just compiles to much more efficient code. Plus, it doesn’t feel like an object.
Other frameworks like Redux propagate functional programming by separating behavior from the state. This makes object-oriented programming difficult. But in my eyes, Redux is just one approach, probably one that’s going too far. Collecting all the data in a single, central store object make it difficult to define the boundaries of your data. Unless you’re storing related data as an object, that is.
Wrapping it up
If you’re a business programmer, you can easily get away without object-orientation. Business applications usually use a framework (which has almost always been written in an object-oriented way), allowing them to concentrate on small chunks of the application logic. This leads many programmers into believing that object-oriented programming is a thing of the past. However, if you’re looking at the bigger picture, it shows that object-oriented programming and functional programming can easily be used together.