COM objects that implement many interfaces can end up suffering from the god object anti-pattern or end up full of tedious forwarding code:
class MyCOMClass
, public CUnknown
, public IFoo
, public IBar
, public IPersistStream
, public IYetAnotherInterface,
, public IAndAnotherInterfaceToo
// etc etc etc
In the most obvious implementation, the class MyCOMClass ends up implementing all the interfaces internally, becomes very large and coupled to details of implementation of each interface. Alternatively MyCOMClass tends to get filled with lots of tedious boilerplate code that forwards the handling of the interfaces to other objects that are focused on the concerns of that particular interface.
Are there any light weight techniques for separating handling of the different interfaces to other internal objects without having to use error-prone COM aggregation or violating COM symmetry requirements for QueryInterface?
My initial attempt at a solution seems to work but feels like a bit of a hack:
Instead of implementing IFoo in MyCOMClass, implement IFoo in a lightweight non-COM C++ class that delegates back to a supplied IUnknown. When QueryInterface(__uuidof(IFoo)) is called, return a FooHandler and supply it with the IUnknown of MyCOMClass as a delegate IUnknown.
class FooHandler : public IFoo
{
public:
SetDelegateUnknown(IUnknown* unk) { m_DelegateUnknown=unk; }
IUnknown* GetDelegateUnknown() { return m_DelegateUnknown; }
HRESULT STDMETHODCALLTYPE QueryInterface(const IID &riid,void **ppvObject) { return GetDelegateUnknown()->QueryInterface(riid, ppvObject); }
virtual ULONG STDMETHODCALLTYPE AddRef(void) { return GetDelegateUnknown()->AddRef(); }
virtual ULONG STDMETHODCALLTYPE Release( void) { return GetDelegateUnknown()->Release(); }
// all the other iFoo methods are implemented here
private:
IUnknown* m_DelegateUnknown;
};
The boilerplate delegate setting and IUnknown implementation could be compacted into a macro like the DECLARE_IUNKNOWN macro in the DirectShow base classes. I haven't found a good way to encapsulate this in a base class.
Thanks for any suggestions.
COM_INTERFACE_ENTRYimplementIDispatchfor me. - Alexandre C.