Page #29 (Interface as an Abstract Base Class)


Dynamic Selection of a Component

Let s look back at our hardware TV and VCR connection. Something seems very obvious any brand of VCR will work with our TV as long as the VCR has a video-out jack that could send video signals according to the same predefined video specifications. The reason is very simple: the TV doesn t care about the VCR; it cares about the video specifications. As a matter of fact, you don t even need a VCR to provide these signals. You can instead use a DVD player, for example. As long as the DVD player has the video-out jack that outputs video signals according to the same predefined video specifications, the TV can display the output.

Where am I going with this? Let us reexamine our TV code. Though we claim that we support dynamic linking, we still always link with VCR.dll a DLL provided by one specific vendor. Why can t we extend our model to use a DLL supplied by some other vendor? After all, we went through a great ordeal to separate interface from implementation. If another vendor supplies a better implementation for the same interface, doesn t it seem logical that we should be able to use the new implementation?

Logical indeed! However, if you recall, the OS loader automatically loads the DLL at the time of execution. We never get a chance to pick the DLL we wish to use. This is because we had linked our client program with an import library that had directives for the OS loader to load the specific DLL.

Obviously, we cannot use the import library mechanism if we wish to pick a vendor-specific DLL during run time.

Recall that the reason we had to use the import library was to resolve symbols that were external to the client program. If you look at our latest interface definition, the only symbol external to the program is the factory method CreateVcr. So, instead of letting the OS loader load the DLL and resolve this symbol for us, if we do it ourselves in the client code, we don t really need to link our program with the import library.

Help is on the way. Microsoft has provided a Win32 API called LoadLibrary that can be used to load a DLL dynamically. Another Win32 API called GetProcAddress can be used to resolve the procedure entry points. With these two APIs, I can write a client-side function, CreateInstance, that will:

  • load the specified DLL

  • resolve extern symbol CreateVcr

  • invoke the method CreateVcr and return the interface pointer

The client code is shown here:

 IVideo* CreateInstance(char* pszDll)  {   // Define a pointer to the prototype of CreateVcr function    typedef IVideo* (_stdcall *CREATEVCRPROC)(void);    // Load the specified library    HINSTANCE h = LoadLibrary(pszDll);    // Obtain the procedure entry point for CreateVcr    CREATEVCRPROC proc =      reinterpret_cast<CREATEVCRPROC> (GetProcAddress(h, "CreateVcr"));    // Execute "CreateVcr" indirectly    return (*proc)();  }  int main(int argc, char* argv[])  {   int i;    IVideo* pVideo = CreateInstance("vcr.dll");    for(i=0; i<10; i++) {     long val = pVideo->GetSignalValue();      cout << "Round: " << i << " - Value: " << val << endl;    }    pVideo->Delete();    return 0;  } 

Now the client code can specify the DLL that it would use during run time. A more innovative and comprehensive TV program would:

  • ask the user to specify the specific VCR DLL to use

  • deal gracefully if the requested VCR DLL is not found (instead of just shutting down, it can still show local broadcasts, for example)

  • not load VCR DLL until the user requests to use it, thus conserving memory resources

  • unload the loaded DLL once the user is done with it, thus recovering the allocated memory space

Not only have we achieved vendor independence, but we have also made the program more robust and less resource-consuming.

Let us see where we stand now. We have defined an interface so that it provides a binary encapsulation that is neither compiler-specific nor vendor-specific. A client is built based on this binary encapsulation. If the interface definition is changed and the client code is not recompiled with the new definition, the binary encapsulation gets broken. In this case, the behavior of the client is completely unpredictable, as we saw in an earlier example.

Even if just the semantics of the interface were changed without actually changing the interface, (for example, by returning a value greater than 40 in our case) the client would still break.

An interface, therefore, is a binary as well as semantic contract that should stay immutable.

But what if we find that the interface the client is using is not adequate, or we just wish to extend the functionality of the interface?


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: