What you are describing is actually much easier to model with abstractions like multimethods (lisp) or type classes (Scala or Haskell). Traditional OOP is fundamentally hierarchical, but the world isn't. This produces the soup of AbstractFactories and design patterns and also is a cause of the expression problem.
Multimethods allow dispatch on more than one argument (versus one ('this') for traditional OOP). Typeclasses decouple behavior from type hierarchies like Java interfaces do, except that they are extensible.
OOP is orthogonal to FP. That said, I'm increasingly of the opinion that it brings little to the table when a language already has typeclasses. I primarily use Scala, which supports typeclasses through traits and implicits. Just about every time I've modelled something in a traditional OOP way with abstract classes and such, it winds up becoming brittle and I refactor the behavior into typeclasses. I got burned by this enough times that I literally never use OOP hierarchies in my code, except to model algebraic data types.
Note that typeclasses and multimethods aren't fundamentally specific to FP, but they are mainly found in FP languages.
Multimethods allow dispatch on more than one argument (versus one ('this') for traditional OOP). Typeclasses decouple behavior from type hierarchies like Java interfaces do, except that they are extensible.
OOP is orthogonal to FP. That said, I'm increasingly of the opinion that it brings little to the table when a language already has typeclasses. I primarily use Scala, which supports typeclasses through traits and implicits. Just about every time I've modelled something in a traditional OOP way with abstract classes and such, it winds up becoming brittle and I refactor the behavior into typeclasses. I got burned by this enough times that I literally never use OOP hierarchies in my code, except to model algebraic data types.
Note that typeclasses and multimethods aren't fundamentally specific to FP, but they are mainly found in FP languages.