Link to home
Start Free TrialLog in
Avatar of ascot
ascot

asked on

COM - One coclass with multiple interfaces or separate coclasses each with one interface ?

I am developing a Com Server that has two interfaces:-
1) Widgets - which is a STL Collection of Widget objects and
2) Widget - which encapsualtes the properties and methods of a Widget itself

I could implement this as one Coclass with 2 interfaces or 2 separate CoClasses in the Same COM Server.

What are the pros and cons of wither approach ? What influences that decision ?
Avatar of rcarlan
rcarlan

Definitely two coclasses: one is a collection, the other is an instance.
In fact, you would probably have three coclasses:
- container with a read-only property that returns a COM interface pointer to the collection
- collection of COM interface pointers to the items, and
- item (Widget, in your case)

If you want to also support automation/scripting, the standard approach is as follows:

interface IWidget : IDispatch
{
      // methods and properties
};

interface IWidgets : IDispatch
{
      [propget, id(DISPID_VALUE)] HRESULT Item([in] long nIndex, [out, retval] IWidget** ppIWidget);
      
      [propget, id(DISPID_NEWENUM)] HRESULT _NewEnum([out, retval] IUnknown** ppIEnumVARIANT);
            // returned pointer is actually a IEnumVARIANT (i.e. IID_IEnumVARIANT) but has to be declared as IUnknown to be able to use the standard marshaller
      
      [propget, id(1)] HRESULT Count([out, retval] long* pItemCount);
};

interface IWidgetContainer : IDispatch
{
      [propget, id(1)] HRESULT Widgets([out, retval] IWidgets** ppIWidgets);
      
      // other methods and properties corresponding to the container
};

This design will enable the use of the 'for-each' construct in languages that support it (e.g. VB, C#, Java, etc).

Radu
Avatar of ascot

ASKER

Thanks for a good answer.  I have the for-each construct working whether I use one coclass or 2 coclasses.
I hadn't thought of the IWidgetContainer interface - how does this help ? What benefits does it provide ?

Also re: "Definitely two coclasses" - why - why not one ?

I've upped the points to 500 in order to get a full answer.
Avatar of ascot

ASKER

Also why     [propget, id(1)] HRESULT Widgets([out, retval] IWidgets** ppIWidgets);
and not
propget, id(1)] HRESULT Widgets([out, retval] IWidgets* pIWidgets);
One of the golden rules in COM design is to define complete yet minimalist interfaces. In other words, each interface should be fully functional, but it should not contain more methods or properties than what is really needed to implement and/or expose the required functionality. This is actually a general principle in OO design: keep refining your logical model until you get to atomic entities - which become your classes.

A Widget and a collection of Widgets are two different concepts. As you said in your initial post: your Widgets have certain methods and properties. These expose the Widget service(s) to clients. A collection of Widgets, on the other hand, exposes different kinds of services - i.e. ability to enumerate over the collection, retrieve a particular Widget (e.g. identified by an index), find out the total number of Widgets in the collection, etc. Thus a different set of methods and properties than the individual Widgets.

You can obviously have one coclass implementing more than one interface. However, this is generally discouraged because certain scripting languages are unable to specify what interface they want to use to talk to a given object. That's why there's a 'default' attribute, and most technical articles and books recommend a single IDispatch-based interface per coclass.

So, both in terms of logical design and in terms of physical design, the two coclasses approach has clear advantages.


The reason for a separate IWidgetContainer interface is that, in addition to the "standard" methods and properties required for the collection pattern, you may want to have other, domain specific, methods and/or properties. The name I used for it was just an example; you would most likely have something that reflects the actual role of this class in your domain (for example, it may be IToolbox). In addition to exposing the collection of widgets, the toolbox may have other properties (e.g. a type or a name). It may also have certain commands (e.g. open, close, add widget, etc). The IWidgets interface, as shown in my previous post, is the standard COM collection implementation - as expected by scripting languages, as well as high-level languages that have a ‘for-each’ construct (e.g. C#, VB, Java). Once again, following the 'complete yet minimalist' principle, it makes sense to split the two apart (i.e. two interfaces and, for the benefit of scripting languages, one interface per coclass – thus two coclasses).

Radu
ASKER CERTIFIED SOLUTION
Avatar of rcarlan
rcarlan

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of ascot

ASKER

Wow - Thanks for taking the time to give a very complete answer describe in a very clear way.
Excellent answer.