Refactoring Com

The COM (Component Object Model) specification states that "interfaces are immutable" and "interfaces are strongly typed."

What challenges does this present for doing ExtremeProgrammingInCom??


Here are some thoughts on the above question:

Interfaces and their methods in a coclass are "mutable" while you are developing a component. What's not is the class ID, type library ID, and interface IDs that you generate using GUIDGEN.EXE that globally and uniquely identify them. In my experiences COM easily facilitates ExtremeProgrammingInCom?. You can change methods and properties and add/remove outgoing interfaces (via connection points or callbacks) as you listen and learn more about your solution further and further into design.

By the time you're ready to deploy even an alpha version of your product, your interfaces should be well iterated and things should be much more solid. Once component reusers are using your components (if they're reusable) then you should commit to setting them in stone (making them immutable) and adding new interfaces and new coclass versions so there are no conflicts or surprises with existing users.

Toward the middle or end of initial implementation, if a major portion of your components should be wrapped up in a single process and distributed across the network via DCOM, then FacadesAsDistributedComponents tends to surface as a natural way to wrap coclasses/interfaces, that are otherwise too granular, with a single less-granular coclass and interface acting as a facade.

I've very recently performed this kind of RefactoringCom and I felt no constraints whatever. In fact I thought it was easier than refactoring C++ based on the fact that there's a more rigid boundary between interface and implementation, and dependencies in both were more well-chunked into separate logical entities.

--PhilipEskelin


Never change COM interfaces. And there's no need to, because a COM object can support any number of interfaces. So, you can create a new interface, just like the old one, except changed as you want it. Then the new object can support both old and new interfaces. This gives you upward compatibility: Old software can use the new object.

But maintaining upward compatibility can slow you down. A quicker way is to use a new interface ID for the changed interface. Old programs should fail, gracefully, because the interface they need is not present. But new programs work just fine. -- JeffGrigg

[Sorry; I stated this too strongly. COM interfaces are really only immutable after you make them available to people "outside your team." A good rule I've heard is that once an interface is used by someone you can't talk to over your cubical walls, you can't change it any more. -- JeffGrigg]


I completely disagree with the above position. This is based on eight years of working with COM and related Microsoft products and technologies and component-based development in general. Perhaps I'm missing the point -- or I'm not understanding you correctly -- but what I understand you saying is that I cannot refactor an interface after a "DesignPhase" even though I'm iteratively designing and developing once I'm in an "implementation phase". I understand you're saying that I can refactor a component's implementation, but not it's interface.

In my experience, this is highly unnatural and impractical. I like to come up with a solid but simple design for an interface or interfaces that are intended to solve a problem. Once I start developing, which probably started happening early in design, I like having the freedom to add methods and properties to existing interfaces until I feel like it satisfies requirements. Once a version is released to the public (e.g., alpha) and the usage of that interface or interfaces is happening out of my project's control, I like to bake off those interfaces as immutable. From that point forward, if I create extended or modified functionality to meet new or changing requirements, I do what you describe above (e.g., create IMyInterface2 as an extension or modification of IMyInterface).

The only place I see this (i.e., changing COM interfaces) not being applicable is if you have a very large project that's geographically distributed and a large-scale architecture has been defined that explicitly outlines interaction protocols that are technically feasible and are proven to meet requirements. I have never been on a project of this size or accuracy (e.g., greater than 16 people, everyone knows the domain), and I hope I never am ;-)

I propose we take this off-line if you'd like to engage in further discussion.

--PhilipEskelin


I agree with Philip, but for the sake of argument: what do you gain? As I understand it, what Jeff is saying is that when you change the interface you should also change the GUID. Philip is saying, don't change the GUID. What do you gain by reusing GUIDs? Are you worried about running out of numbers?

Side note for strangers to COM: The GUID is a GloballyUniqueIdentifier, some 128 bits that are the true name of an interface or a COM object. That is to say, you don't look up interfaces by searching for the string "IMyInterface" in a registry; you have some code like:

   DEFINE_GUID(IMyInterface,0x7C0FFAB2L,0xCD84,0x11D0,0x94,
       0x9A,0x00,0xA0,0xC9,0x11,0x10,0xED);

and then look for the bit pattern. When you change an interface you can leave the name the same but change the bits. You don't have to support the old interface. Programs that try to use it will be searching for the old bit pattern, which will fail gracefully. -- DaveHarris

Actually, Dave, what sucks about this situation is if I changed an IID then it will take programmers who are using my component a long time to figure out why CoCreateInstance() works, but why QueryInterface() fails if their using generated code that still references the old IID. And it's not always so graceful. Now it's true that the project can do CM better or manage their dependencies and build process better, but that's not always their luxury. --PhilipEskelin


Good question. I suppose I could change it but I'm too lazy ;-). Leaving the GUID the same hasn't hurt anything for me. And I don't know the value in updating it. For example, unless I've memorized my GUIDs, or am really anal about my registry, I'd be leaving bogus CLSIDs, TypeLib? IDs, and IIDs sprinkled throughout my HKEY_LOCAL_MACHINE hive.

For COM neophytes: COM gives unique GUIDs to coclasses (equates to the physical unit of instantiation in a component's implementation) called CLSIDs, unique GUIDs to each interface called IIDs (interface being a way to look at an object), and unique GUIDs to type libraries called TypeLib? IDs (they are used by the universal marshaller and by clients to reflect upon an interface's methods, properties, type info, and other information).

Once I deploy -- whether into pilot (e.g., alpha or beta) or production -- I think of all GUIDs and methods/properties available in interfaces as solidly immutable. Any additional changes or refactorings would be available to clients through new interfaces containing methods and properties needed to access it.

--PhilipEskelin


My situation has been one of writing "library" code used by other independent teams. The other teams are typically in other rooms of the same building, but release schedules are largely independent of ours. Also, there was a strong desire to "mix and match" different versions of software in releases. So I have a much stronger feeling for the need to preserve upward compatibility of interfaces, and protect the system from the failures that can occur if components have mismatched interface versions.

Realize that each COM object has at least two globally unique ids (GUIDs): A "class id" and one or more "interface ids." I usually keep the class ids constant, and vary the interface ids over time. "An interface cannot be changed after someone in another group starts to use it." By keeping the class id constant, I can provide compatibility for older programs that have not upgraded to the new interface. (See PhilipEskelin's comment above.)

My experience has been that when we slap together programs using incompatible versions of the same interface id (a violation of the COM spec), the program crashes. This leads to days of finger-pointing, customer relations problems, hot tempers, and (the part that actually gets something done ;-) intense debugging. It's always an unpleasant experience. -- JeffGrigg

I think we're talking about two different things here. I am talking about changing the methods and properties of interfaces BEFORE YOU EVER DEPLOY IT. What you describe above is true if you've deployed an alpha, beta, RTM, or production version of a program. But when you are building it and still designing it, it is unnatural and against the grain of XP if you can't refactor interfaces. And I don't "slap together programs." Today I am working on a highly critical project that will be deployed globally and is reliable, scalable, component-based, reusable, and flexible. Seriously, WE WON. And changing interfaces during design and development helped us do that. --PhilipEskelin

Right. Exactly. Perhaps we should refine the transition between "change it as much as you want" and "don't change it ever again:" There is some point beyond which you must stop changing a COM interface's definition, or you risk having things crash when integrated. I like to say that once it's used by someone you don't control (someone who's not working very closely with you), then it's frozen; any change (even adding functions "at the end") can cause grief. However, with better version control and a coordinated release strategy, I'm sure that the cutoff line can be pushed out.

(My big issue was convincing coders that upward compatibility was a good thing. Other issues were really secondary.) -- JeffGrigg

I agree. In fact, if you look at what I said when I first replied to you, you'll see this sentence:

Once a version is released to the public (e.g., alpha) and the usage of that interface or interfaces is happening out of my project's control, I like to bake off those interfaces as immutable. From that point forward, if I create extended or modified functionality to meet new or changing requirements, I do what you describe above (e.g., create IMyInterface2 as an extension or modification of IMyInterface).

What I meant is compatible with the conclusion you came to. --Phil


On the C++ side, Microsoft has made it fairly clear what to do when your interface changes in any way: change the GUID. The only people at Microsoft that don't recommend this is the Visual Basic team. VB lets you keep the same GUID as long as the changes to the interface do not break backward compatibility. So, in VB, things like adding a new method will not force a GUID change. The VB team's interpretation of COM is still fine. The rule of thumb that should apply no matter which Microsoft recommendation you take, however, is that if you break compatibility with the old interface, change the GUID. I can't see the value in leaving the GUID the same when the interface changes even while in development; it is bad form and reeks of laziness. -- JohnPerkins


While I'm in development, I often change the interfaces several times a day. No one but me sees those changes. Why should I take the pain of generating new GUIDs and changing them in all the necessary places?

As long as you don't get confused yourself with which version goes with which (something that can happen when you have a lot of components) then no problem. But once you release changed interfaces to anyone but yourself (this includes checking things into source code control) then the GUIDs should be different to reflect the fact that the interface is different -- JohnPerkins

And speaking of VisualBasic... it has a nice habit of incrementing the interface version every time you do a rebuild, so your registry quickly gets cluttered with MyComponent? version 82.0, 83.0, 84.0 and so on ad nauseam. I hate this. -- DmitryJemerov


CategoryRefactoring CategoryMicrosoft CategoryComponentObjectModel


EditText of this page (last edited May 6, 2008) or FindPage with title or text search