I was reading this the other day and I recognised Past Mozilla Mistakes: two as something that quite a few (if not all) of my clients have made…
If you’re writing in C++ then COM is just an interface layer. It’s great for providing access to an object model to loads of non C++ consumers but, in my opinion, it’s a lousy thing to base your entire system on. COM is a complex thing, considering how much work it does for you, the rules aren’t that onerous, but they tend to corrupt nice, clean C++ code if you let them. I’m convinced that COM should stay at the edges, use it as an interface layer and write everything else as if COM didn’t exist. As usual this means that you need to think a bit more; trust me, it’s worth it.
All too often I’ve seen projects where every object is a fully fledged COM object; no matter how simple, or how ‘internal’ it is, it does
IUnknown. Often the creators are proud of this; I’m a COM programmer, not a C++ programmer. Yeah, right… All that says to me is ‘I can’t decide where it’s appropriate to use a technology and where it’s inappropriate, so I’ll use it everywhere’.
These ‘single hammer’ developers are making life hard for themselves. Once again a failure to make decisions that limit what you can do makes the resulting code more complex. Everything is a COM object because thinking about what should be a COM object is harder than just making everything a COM object. Once you head down this route you’re saying goodbye to functions that can return results; you’re saying goodbye to using
const to specify that you aren’t going to change this value that you just got by calling
GetThing() because it has to return an
HRESULT and return the thing you’re interested in by reference… You’re saying hello to the wonderful world of manipulating everything as variants and safe arrays and mostly using automation types (but not always, it’s never always because there’s usually someone on the team that slips in a quick hack here or there because they’re lazy and doing it the COM way can be such a pain).
Of course there’s always someone who has to say ‘but it’s the only way to do it’. They’re wrong. It’s not. Even if the thing you’re writing exposes much of itself via COM you can still decide how far the COM influence needs to go. You can still use COM just at the borders. On the inside; you can wrap COM objects as soon as you use them (like a grown-up’s version of
#import that requires thinking) and, guess what, you only need to do this once… If you use the COM version of the MS XML Dom you can write some wrappers for the bits you use, they can do all the
HRESULT checking, argument translation, etc and provide a nice clean interface that the rest of your code can use; if you’re feeling clever this interface needn’t ever expose the fact that it’s using COM inside and you could switch to a non-COM version of the implementation if you ever needed or wanted to… On the outside; you can write most of your code in normal C++ and then write a COM layer to expose it to COM clients, and, perhaps, a managed C++ layer to expose it to .Net weenies and, if necessary, package it as a normal DLL or static lib for those legacy C and C++ folk. Even if you don’t need to take all of these routes at the start, if you don’t bet the farm on COM you can be flexible later on…
This may come across as a tad anti COM. It’s not. I think I’ve done my fair share of COM programming in the past (I still have the scars from the OLE-DB providers they forced me to write); and the thing I’ve learned is that COM is good for what it’s good for, and that is interfacing. It’s a relatively expensive layer (in terms of complexity) and as such you should know where it’s appropriate to use it and know the things you’re giving up if you decide to go that way. Just as you don’t want all of your code to be dependant on MFC, or managed C++, or the ‘Reuters library’ that you use in this one corner of the application but that happens to provide a ’nice’ definition of a type that happens to be almost what you need over here; so you don’t want all your code to be dependant on COM (and even less so on ATL).
Interfaces are important; decide where they stop.