Object-oriented – C++ Design: Functional Programming vs OOP

cdesignfunctional programmingobject-oriented

Design Question

Recently, I've been doing more and more FP in C++, mostly in the form of function templates and lambdas, and heavy overloading of a single function name. I really like FP for some well-defined operations, but I've also noticed that as with OOP, one could easily fall into anti-patterns of spaghetti-code if one isn't careful (eg circular dependencies, which is a bigger issue for state-based code).

My question is, when thinking about adding new functionality, how do you decide between using a FP-paradigm or a OOP-paradigm?

I'm sensing it may have to do with identifying the invariants in the problem or in the design, but I'm not sure.

For example, without an OOP model/simplification of the real world, it may not be immediately obvious what a Dog or a Cat class is (what are its states? what are its methods?). Otoh, from a FP POV, a Eat() function simply allows an Animal to turn Food into Poop and Energy. It's harder to imagine Eat() being anything else (for me, at least).

I'm looking more for C++ answers, but this question could apply to any sub-module in any language that can handle multiple paradigms.

Best Answer

I think that OOP lends itself best to when you have state that you can't destroy and re-create on a whim, or where that's a bad model- for example, if you consider 3D rendering, then the state of the renderer is not trivially destroyed and recreated, and it would be suicide for your performance to do so each frame.

In C++, then you need to add the fact that it doesn't have garbage collection and many FP techniques depend upon that for remotely acceptable performance.

Otoh, from a FP POV, a Eat() function simply allows an Animal to turn Food into Poop and Energy. It's harder to imagine Eat() being anything else (for me, at least).

That's completely equivalent to the OOP variant. There's no meaningful difference between

struct Animal { ... };
std::tuple<Poop, Energy> Eat(Animal*, Food);

and

class Animal {
public:
    std::tuple<Poop, Energy> Eat(Food);
};

A functional variation would return a new Animal which has eaten.

Typically, I recursively divide my program to obtain modules/submodules/classes/functions and member functions, then I often attempt to implement those functions or member functions functionally if possible.

Related Topic