Now that we have learned about functional programming and its benefits, let’s revisit object-oriented programming and see how we can do better with functional ideas.
Recall from Declarative vs. Imperative Programming that object-oriented programming is primarily imperative, where we tell the computer what to do, while functional programming is primarily declarative, where we define properties and relations, and let the runtime figure out how to compute what we want. We demonstrated the differences with two versions of the factorial function. The declarative version was clean and simple, while the imperative version was “busy” with mutations, making it harder to understand and prevent bugs. Those problems multiply if your whole code base is like that.
We’ve seen other reasons to
avoid mutability. Mutable objects are not thread-safe by default and it’s
easy for clients to change their state outside our control. Hence, we
should make our objects immutable by removing setter methods and by
final. We should
create new instances when the state changes and we should rely on
persistent data structures for making efficient
copies of large collections. We should avoid representing elaborate domain
model object “graphs” in memory by limiting the parts of our domain models
that we actually implement.
Sometimes we can’t avoid
mutation. Since Java doesn’t perform tail-call optimization,
declarativeFactorial won’t ...