Page #30 (Dynamic Selection of a Component)


Extending the Interface Functionality

As the needs of the clients evolve, the vendors at some point have to provide more functionality to the clients. In our scenario, let s say the VCR vendor decides to provide support for a newer set of specifications called S-Video. This new signal provides better quality video pictures than the old one. Obviously, a newer model TV would be needed that could support this new signal. The VCR vendor, however, has a dilemma:

  • If the vendor changes the current interface to provide an S-Video signal instead of a Video signal, all the existing TV clients would break, as they do not know how to handle the new signal type.

  • If the vendor publicly states that she is moving towards the new S-Video signal and will not support the old signal anymore, then she will lose her existing customers and many new customers who do not have the newer model TV that can support the newer signal.

Ideally, the VCR vendor would like to support both the signal types. The TV manufacturer has a dilemma of its own:

  • If the TV supports only S-Video, there are thousands of VCRs out there that still provide the old-style Video signal and thus will not work with the newer TV.

  • If the manufacturer decides not to support S-Video, he is losing its market to its competitors.

Ideally, the manufacturer would like to probe the VCR to detect if it supports the S-Video signal. If the support was found, then it could use the new signal type. Otherwise, it would use the old signal type.

Let s extend our simulation program to deal with multiple interfaces and define a new interface for S-Video that supports a method to return the S-Video signal.

 // SVideo.h - Definition of interface ISVideo  class ISVideo  { public:    virtual long _stdcall GetSVideoSignalValue() = 0;    virtual void _stdcall Delete() = 0;  }; 

Note that, as established earlier, an interface always has to support the Delete method.

Using multiple inheritance, we can now add support for this new interface to our original implementation class, CVcr, as shown in the following class definition:

 class CVcr : public IVideo, public ISVideo  {   ...  } 


It was our choice to support both the signals in our simulation program. It is up to the VCR manufacturer to support just the IVideo signal, just the ISVideo signal, or both signals.

Also note that the restriction of using multiple inheritance was placed on the interface definitions, not the implementation. The implementer is free to use multiple inheritance, as we did in our case.

We now need to add the semantics of probing for a signal type. Let s define a method, Probe, that takes a string input to specify the interface we are probing for. If the interface is found, the method will return the corresponding interface pointer. The client code snippet shown here illustrates the idea:

 IVideo* pVideo = CreateInstance("vcr.dll");  ISVideo* pSVideo = pVideo->Probe("svideo");  if (pSVideo != NULL) {   // use S-Video  }else {   // use Video signal  } 

The code requires that function CreateInstance return the IVideo interface. This would certainly not work with those VCRs that implement only the ISVideo interface.

If the prototype of CreateInstance is changed to return the ISVideo interface, then it would become incompatible with those VCRs implementing just the IVideo interface.

An easy way to solve this dilemma is to define a more general interface, IGeneral, that doesn t really do much except to provide the probing semantics for the other more meaningful interfaces such as IVideo and ISVideo. Each VCR would then implement this general interface and return a pointer to this interface when CreateInstance is called.

If all other interfaces are derived from this general interface, then every interface will automatically support the semantics of probing. Also, as all interfaces are required to support Delete semantics anyways, it makes a great deal of sense to move this method to the base class as well, as shown in the following code snippet:

 class IGeneral  { public:    virtual IGeneral* _stdcall Probe(char* pszType) = 0;    virtual void _stdcall Delete() = 0;  };  class IVideo : public IGeneral  {   ...  };  class ISVideo : public IGeneral  {   ...  };  extern "C" IGeneral* _stdcall CreateVCR(); 

Note that method Probe returns a pointer to the IGeneral interface instead of a specific pointer to the IVideo or ISVideo interfaces. A function can have only one return type; this is a syntactic limitation of C language (or any other language I can recall). The client code just has to reinterpret the type depending on the specified pszType parameter.

 // TV client code  int main(int argc, char* argv[])  {   IGeneral* pVCR = CreateInstance("vcr.dll");    // Use S-Video if available    IGeneral* pGeneral = pVCR->Probe("svideo");    if (NULL != pGeneral) {     ISVideo* pSVideo = reinterpret_cast<ISVideo*>(pGeneral);      UseSVideo(pSVideo);      pSVideo->Delete();      return 0;    }    // S-Video not available. Try old "video" type    pGeneral = pVCR->Probe("video");    if (NULL != pGeneral) {     IVideo* pVideo = reinterpret_cast<IVideo*>(pGeneral);      UseVideo(pVideo);      pVideo->Delete();      return 0;    }    // Neither S-Video nor Video    cout << "This VCR does not have the signals this TV supports" <<  endl;    pVCR->Delete();    return 1;  } 

Here is the revised definition of our implementation class, CVcr.

 class CVcr : public IVideo, public ISVideo  { public:    // IGeneral interface    IGeneral* _stdcall Probe(char* pszType);    void _stdcall Delete();    // IVideo interface    long _stdcall GetSignalValue();    // ISVideo interface    long _stdcall GetSVideoSignalValue();  private:    // other member variables and methods not shown for brevity  }; 

Consider the implementation of CreateVCR code:

 IGeneral* _stdcall CreateVCR(void)  {   return static_cast<IVideo*>(new CVcr);  } 

When returning the base interface IGeneral, the implementation statically casts CVcr instance to IVideo. This is because directly casting the instance to IGeneral is ambiguous, as IVideo and ISVideo are both derived from IGeneral. To help the compiler resolve the ambiguity, the implementation has to pick either IVideo or ISVideo. It doesn t matter which one.

Had the interface IGeneral been declared as a virtual base class for both IVideo and ISVideo interfaces, the ambiguity would have disappeared. However, using virtual base classes introduces compiler dependencies, as this C++ language feature has no standard implementation but many proprietary implementations.

Now consider the implementation of the Probe method:

 IGeneral* CVcr::Probe(char* pszType)  {   IGereral* p = NULL;    if (!stricmp(pszType, "general")) {     p = static_cast<IVideo*>(this);    }else    if (!stricmp(pszType, "video")) {     p = static_cast<IVideo*>(this);    }else    if (!stricmp(pszType, "svideo")) {     p = static_cast<ISVideo*>(this);    }    return p;  } 


Technically, our VCR implementation supports three interfaces, although there are only two meaningful interfaces for TV users. The third interface, IGeneral, does the magic of obtaining any of the other interfaces, or, by extension, obtaining one interface from another interface.

The previous code appears to be a classic case of using the C++ language feature of run-time type information (RTTI). Why did we not use RTTI instead of going through this extensive semantics of defining and using Probe ? Because RTTI is yet another C++ language feature that is not com-piler-independent.

Hang in there. We are almost done. The next problem has to do with lifetime management of the VCR object.


COM+ Programming. A Practical Guide Using Visual C++ and ATL
COM+ Programming. A Practical Guide Using Visual C++ and ATL
ISBN: 130886742
Year: 2000
Pages: 129 © 2008-2017.
If you may any questions please contact us: