A couple of years back at university, I attended a class called
"Advanced Object-Oriented Programming" (or something to that effect),
in which Design Patterns were discussed as well as the short-comings
and limitations of unmitigated object-orientation. Or maybe just
the short-comings and limitations of the popular Java-brand of
object-orientation (the lecture was held by a Smalltalker, after all).
I cannot remember all the topics presented there (it included dynamic
typing, multi-method dispatch, composition as opposed to inheritance),
but I suppose the gist of it was to raise an awareness that OOP as
taught in Programming 101 may
not be the perfect fit to all problem domains and is beginning to
show its age, just like the methodologies it has replaced.
Recently, I find myself making less and less use of two concepts
that I had previously thought to be fundamental to OOP: Mutable properties and
subclasses. The interesting thing about this is that I do not
think I am defecting OOP, but rather that these two concepts
are more marginal than they are introduced as.
As for mutable properties, Functional Programming (where there
are no variables at all) has recently had a bit of of a comeback, for
example in the form of Haskell, Scala or Erlang. Unfortunately, I have so far
utterly failed to wrap my head around the idea (and the same goes for
Logic Programming). I guess, I am more of a procedural guy that needs to
tell
the computer what to do, and how to do it (including it what order).
However, I have come to see the enormous benefits that arise when you
can assume/assure that things never change, especially when it comes
to asynchronous execution, shared state, or memory management. So while
I still cannot grok Haskell, I totally understand why
java.lang.String is implemented the way it is (and want
more of that, for example for arrays).
Subclasses always seemed to be the most important feature of OOP:
You can subclass an implementation to inherit most of its features and
change only a few. Instances of your subclass can be used in all places
where the original class was expected, and the calling code does not
even need to know what is going on.
As it turns out, this replacability
(called polymorphism) can be achieved by means other than a class
hierarchy. In Java, the obvious alternative is an interface. Keeping
interface separate from implementation seems like a good idea. And
Java interfaces also have the advantage that you can have many
at the same time, whereas you are left with a only single superclass.
There are other ways to reuse code than subclassing, too.
A good one is using delegates. Delegates are objects, so you
can still get the benefits of encapsulation and polymorphism. In fact,
even more so than with a superclass, because you can switch one delegate
implementation for another one (even at runtime), whereas you cannot
change your superclass. And since you can obviously have many delegates
for the different tasks, you can "inherit" code from multiple places,
something that you cannot do with a single superclass. I also find that
delegate classes are extremely focused in doing just one thing and of a
high reusability across the code base, whereas an abstract class using
the Template Method pattern can end up as an
unwieldy assortment of utility code not really related to its core
"interface" -- coode that no one else except the subclasses can make use of, and
the which is being forced unto those subclasses even if
they wanted to do things differently.
So at this point, I consider the two main features of OOP to be
encapsulation and modularity (the unit of which should be a package in
Java rather than a single object), and polymorphism. Everything else
(subclassing, the ability to have classes in the first place, strict
typing...) are just implementation details.