Number 1 --
COM is dependent on reference counting...
and in a noticeably screwed up manner. When you add a new Interface
to an object, you increase its reference count. This is automated
through an 'AddRef()' call. However, if you are done with a particular
interface, you have to 'Release()' the interface yourself in order to decrement
the reference count. I'm not necessarily saying that we should automate
reference releasing (although it's more than possible by just having a
default destructor that executes a Release)... You could just as
well NOT automate the AddRef's. Either way would be nice because
it's symmetric. Having an asymmetric automation is a surefire way
to give rise to programming error.
Just note that this only one edge of the
reference counting blade. A side effect here is that a COM object
can increase its own reference count secretly. So you can Release
an interface, but it will still hang around. And of course, the opposite
is possible. It's quite possible to Release an object one too many
times and it will disappear for real. If that happens, you'll get
a lovely core dump the next time you reference the interface.
Well, the obvious reply is that "Don't
DO that. Write better code." Ummmmmmmmm.... This is coming from Micro$oft,
who believes that Moore's Law is something that was made to be squelched?
Yeah... better code. Practice what you preach, COM monkeys.
Number 2 --
COM encourages aliasing. As in,
it encourages the practice of giving one item multiple names. Basically,
in COM, you create Interfaces to existing objects. Once you've done
that, you've aliased the object. The purpose of creating more Interfaces
for this object is to allow new functionality for the object without doing
anything to the old interfaces. It sounds like a good idea except
for a few things. One, it causes code-bloat, but apparently that's
harmless. Two, you've aliased the object and can potentially get
confused about reference counts... we have to assume people are stupid,
after all -- Micro$oft has done that so far, and look at their success.
The other problem is that it's fairly common (especially in DirectX) to
break old interfaces when putting in new ones. COM does say that
you can recode the guts of an interface all you want as long as the functionality
does not change. That's an utter joke. There's only two real
ways to do that -- Either you bugfix the guts, or you change the
performance of the guts. Neither of which is advocated. If
you're not doing either of those, then it's an utterly cruel joke.
You simply can't touch your old interface.
The other problem with this philosophy,
though, is the example that you can bugfix/update existing functionalities
of old interfaces, as well as adding new functionalities. By the
rule of COM, we cannot put the updates of the old functionalities into
the old interfaces even if nothing changes in parameter space and such.
The only thing we can do is create an entirely new interface. Moreover,
if we add new functionalities that are completely unrelated to old ones,
we still have to create a new interface. This is not just code-bloat,
but unjustifiable code-bloat. The problem with Micro$oft's attitude
to code-bloat is that they don't have ANY reasons... only excuses.
There are no exceptions, btw.
Example of Code-Bloat? DirectDraw2
and Direct3DRM2 objects. Instead of just adding features through
a new interface, they actually hold implementations of EVERYTHING that
existed in their predecessors DirectDraw and Direct3DRM. Very few
behaviour changes were in there; a lot was cut & paste. WHY??!??!
That goes against the very idea of COM even by itself.
Well... time for an example...
Take the DirectX 5 Direct3D Ramp renderer,
which for some historical reason is called a "Device." Someone got
a brilliant idea that it would be faster if the ramping was calculated
only once for the one time that the texture was bound. Problem is
that sometimes, people animate textures or apply multi-texturing (lightmapping,
for instance). Shimatta!!! The end result now is that your
animated textures in everything from DirectX 5 through 7 (not sure about
8) will fill in black for all colors in animation frames that were not
in the first frame. Which means that pre-DX5 Ramp modes don't work and
there is no workaround.
Ideally, there should have been a Ramp2
interface for the new ramping mode, but that didn't happen. And this
is a perfect example of how Micro$oft's big statement about COM is a major
fallacy. A position paper from M$ says that "COM solves the software
versioning problem." Perhaps if every software engineer were perfect in
this world, it would do just that. But we cannot say that COM produces
no problems. For everything that it solves, new problems arise. That's
a general rule in programming, I know... but again, that's not a
reason. It's an excuse. The old approach of providing new .dlls
and putting new version filenames worked far better and created less interference
between the old and new.
This creates another question. What
constitutes a broken interface? COM works on interface contracting,
so what do you do when a contract is broken. Do you fix the broken
interface, or do you leave it there, still broken, and create a new one
that ISN'T broken? Do you do what the standard answer to this question
says or do you exercise some power and actually decide to do the
opposite?
The huge problem lies in Micro$oft's attempts
at answering that. Whenever some COM util has a broken interface,
they quote the infinite authority of compatibility. They claim that
something shouldn't be fixed because it risks compatibility, but the fact
is that they just don't want to fix it. Other times, they're rather
happy to break something because it is apparently an all new version and
so compatibility is non-existent, and they can simply ship a broken product
and tell you to go to their website to download bugfixes. "While
you're downloading, buy all our other products or we will charge them to
your credit card for you. Have a nice day." The icing on the cake
is that they even fail to properly document across their own versions of
their own SDK's. Isn't this EXACTLY the sort of thing that COM was
supposed to prevent?
There ARE fixes to the aforementioned
problems. For instance, you can have your own variables that keep
track of reference counts for you. But this is a C++ style fix (smart
references). COM is supposed to be language and OS neutral, so this
sort of a fix is rather defiant. And we also have to remember that
anything from M$ that says "OS neutral" is a boldfaced lie. This
is not a value judgment -- M$ would simply be cutting away at their own
bottom line by making something cross-platform. Besides which, if
COM was such a great solution, why on Earth do we have to put bandages
on it?
You also have one more issue to deal with
the fact that not everything is a COM interface. In particular...
DRIVERS... Sometimes, they like to masquerade as COM interfaces,
but this duct tape approach is not very convincing. The thing is
that drivers change all the time. Why aren't there new COM interfaces
for every new release of a driver? Why is it that drivers don't balloon
to infinity, which is apparently an okay thing because... because...
Moore's Law! SO THERE! Got a problem wit' dat?!?? Well, NP-HARD,
too!!
Don't get me completely wrong. The
idea of reusable interfaces is good. It's just that we need a better
and less broken solution than COM. If a way could be found to get
around the aliasing issue, that would clear up a good deal of the related
problems. You can't say that something solves everything when it
creates problems for less-than-perfect programmers. When you get
right down to it... we're all less than perfect.
Well, it boils down to this -- you rid the world of COM...
or you rid the world of people.
Hmmmmmmm... damn... that made it tougher. I kinda
like both ideas.
View the Soapbox archives.
Back to my home page.