In addition to the disadvantages mentioned by Ahmed, here are a few
more.
1. O-O programming requries more organization and greater effort spent
on designing the architecture up front, at least if you want to avoid
massive code churn (rewriting). In a third-generation language like C,
if you get the design wrong, you can usually fudge the code and mash
things around to get them to work out. In C# or C++, if you screw up
the class hierarchy, you are truly screwed.
This may sound like you just have to take more time up front for a
later payoff, but remember that "architecture is a risk" (read "The Big
Ball of Mud" by Foote and Yoder:
http://www.laputan.org/mud/mud.html ).
Sometimes you don't know enough about the problem, or you don't know
the language well enough to produce a good architectural design up
front. What do you do then? Well, you take a stab at it, then discover
later on that you were wrong, and that as a result you have to
practically start from scratch, because in O-O the really important
decisions are made very early in the development cycle (during analysis
/ planning rather than during coding).
2. Inheritance can make code harder to read. When you have classes
inheriting from classes inheriting from classes and interfaces, you can
end up threading through code looking for which method is _really_
being called. In C, you can get away with using a simple editor and a
command-line compiler. In C# you have Intellisense, which is not just a
nice feature--you _need_ it, because without Intellisense and automated
documentation you really have _no_ idea what's going on inside a class.
3. Inheritance and polymorphism mean that you have to understand the
design, the programmer's original intent, very well before you start to
maintain the code. In a 3GL world it was important to understand the
original programmer's mindset to some degree, but I managed to work on
very badly written programs by biting off small chunks and rewriting
them. In C#, the code isn't nearly as important as the overall design
of the class hiearchy, which requires maintenance programmers to do
more studying of the code before they get up to speed.
Again, when you have classes inheriting from classes inheriting from
interfaces, you had better understand why all of that was built the way
it was, or making a small change "here" will ripple throughout the
entire class hierarchy and cause changes in behaviour in the least
expected places.
4. I'm trying to think of a disadvantage of encapsulation, and I'm
having trouble, given that I did it in C for 15 years before I even
touched an O-O language. It does require a lot more code, it's true: I
find that programs that employ proper encapsulation are maybe 30%
bigger than ones that don't. However, they _work_, and they're _cheap_
to maintain, so I see the tradeoff as worthwhile every time.
Of course, in most jobs if you want to look like a star you just toss
encapsulation and write crappy, quick-and-dirty code to get the job
done. You come in ahead of schedule and under budget, your boss thinks
you're a genius, and you dump the whole ugly mess on the maintenance
team and move on to your next highly visible project. So long as you
can keep one step ahead of the ticking time bombs you're creating in
the process, and blame the huge number of latent bugs on the
maintenance team (because by now you're on an unrelated project),
you're laughing. Of course, this doesn't work at all if you're on a
small team that maintains what they build, which is why I could never
bring myself to do it. I know more than a few guys who spun themselves
into top consulting jobs / management positions doing that, though. So,
I guess one disadvantage of encapsulation is that although it's the
"right" way to do things, some people who ignore it will get the corner
office while you're still toiling in the programming team.
5. Encapsulation hides details about how a class implements its
functionality, which is good from a maintenance and understanding
standpoint, but also makes it easy to write programs that waddle into
memory like enormous, bloated sows, and sit down with a thump, sending
all of your other applications scattering for the swap file. Because
you don't directly know what an object needs in order to do its job,
you don't really know what are the consequences of making 10,000 of the
things. Try sucking a whole lot of data from a database into a DataSet
object in ADO.NET to see what I mean. Ouch.
This isn't necessarily a bad thing, but again it's a learning curve,
and a lot of programmers don't bother learning: they just build fat
piggish programs that use up 1GB of memory and tell their bosses to
shell out for more RAM. What I mean is that if you really need 1GB of
memory, then you need it. In the 3GL days I wrote one or two programs
that used that kind of memory, but only one or two, and only for
specific reasons. In the O-O world it's just too easy (because of
encapsulation) to write code like that; it takes training and
experience to write lean applications.
5. The same goes for CPU cycles: because encapsulation hides how a
class does its work, it also hides how much code has to run in order to
do that work.
6. Inheritance and polymorphism bring with them a strong temptation to
overgeneralize everything, "because you can." It's far easier for
programmers to go nuts and try to design some new class that is vast
overkill for the problem they're trying to solve. The problem here is
one of efficiency: frequently these "do anything" classes are at the
top of a large pyramid of other supporting classes that together make a
huge, ungainly monster when in fact a simple little tool would have
sufficed. Here we're talking about the guy who builds a whole new
formatting language in order to print strings to a log file, rather
than just using String.Format(). It's just easier to do in O-O, and
easier to do inefficiently.