What Is OLE? (A Reprise)

I've defined OLE as a unified environment of object-based services. OLE's services can be customized and its architecture arbitrarily extended through custom services. The overall purpose of such an extensible service architecture, as it is called, is to enable rich integration among components and the applications we know today. Rich integration empowers not only developers but also end users to build a custom application out of separate components, in much the same way that many custom electronic devices are built from smaller electronic components. The electronics industry has known how to work with components for many years. The software industry is just entering this arena. OLE is the first and strongest technology to actually make it possible.

At the core of OLE is the Component Object Model, or COM. COM defines the notion of interfaces—the means through which the client of an object communicates with the object. Each interface is a semantically related group of member functions. The interface as a whole represents a feature, and the member functions in that interface represent the various operations that make up that feature. When an object implements an interface, it provides a table of pointers to functions through which a client can access the object's implementation of the feature represented by the interface.

The concept of an interface fully supports the fundamental notions of object-oriented architecture: encapsulation, polymorphism, and reusability. In this concept, we recognize that inheritance, typically the bastion of what it means to be object oriented, is merely a means to the ends of polymorphism and reusability. In this book, I have endeavored to show that these two notions, along with encapsulation, are the truly important ones. When we realize the core needs of object orientation, we discover that inheritance is not the only means to polymorphism and reusability and that these other means have their appropriate uses depending on the problem one is trying to solve.

The fundamental problem that OLE solves is the integration of binary components, unlike programming languages, which primarily solve source code reusability problems. Accordingly, there are very few reasons to see OLE and programming languages as antagonists. Instead, they work together to achieve software solutions that have never been possible before.

Besides providing a means to make binary objects, OLE also improves on the basic object-oriented notions through the concept of the interface and the fact that the omnipresent QueryInterface function allows an object to implement multiple interfaces. Because each interface represents a feature, an object can provide support for as many features as it wants simply by implementing the appropriate interface. This idea is probably the most important contribution that OLE makes to object-oriented programming: What QueryInterface allows us to do is ask an object, at run time, whether it supports a particular feature. This allows us to write client code for that object that can respond to the absence or availability of a feature in an appropriate manner. The strength embodied in this capability is that whenever an object is updated to support a feature it had not supported before, any and all clients that are already aware of that feature, even those that are already running, can immediately take advantage of the presence of that feature on the updated object. OLE allows you to drop a new object implementation into a running system and have it be immediately integrated with existing clients. Not only that, but you can also make incremental changes to clients. When those clients are deployed in a running system, they can immediately take advantage of object features that they had not used before. We call all of this the robust evolution of functionality over time.

Interfaces allow an object's designer to factor that object's functionality and content into groups of a higher level than individual member functions. Traditionally, factoring an object's capabilities resulted in a set of member functions and data members for an object. Together, the functions and data members were referred to as the object's singular interface. OLE provides an entirely new means to factor an object's capabilities into discrete and higher-level features first and then to factor those features into the necessary functions and data members. This introduces a higher level of polymorphism than is traditionally understood with object-oriented programming—the notion of objects that conform to a prototype. A prototype is a definition of a set of interfaces in which all object classes that implement the same set of interfaces can be used polymorphically across those interfaces. So not only are two objects polymorphic along one dimension when they implement a single interface in common, but they are also polymorphic across multiple dimensions when they implement the same set in common. This means that applications and tools need to know only how to work with the prototype in order to work with any of the classes of objects that conform to that prototype. Whether those objects were created three years ago or will be created three years in the future doesn't matter. Combined with QueryInterface, this polymorphism solves the real-world problem of deployment of new components over time.

OLE's other major contribution to object-oriented computing is that a client can employ the services of an object without regard to the distance or boundaries between the client and the object. A client, as we've seen, always calls functions through an interface pointer that is meaningful to that client's own process. When the object is fully implemented inside a dynamic link library (DLL), such calls go immediately to the object's own code. When the object is "out-of-process"—that is, implemented inside an executable (EXE) on the same machine or any module on another machine altogether—an in-process proxy does whatever is necessary to communicate with the real object's implementation wherever it resides. This is called Local/Remote Transparency, with which a client can use interface pointers in its own process to access objects in other processes and on other machines. The client doesn't concern itself with the distance to the real object—OLE, particularly COM, makes it all transparent.

The idea of interfaces, along with Local/Remote Transparency, is at the center of OLE, even at the center of COM. Literally everything else in OLE is a service built on this core, a way to customize that service, or a means to create a custom service that extends the architectures. In this book, we've seen that OLE provides fundamental services for type information, custom service management, structured storage (Compound Files), naming and binding (monikers), data caching, and data exchange through the OLE Clipboard and OLE Drag and Drop. In addition, OLE provides a great number of minor services typically exposed through a few helper functions.

With these services in place, OLE provides the means for different components and clients to communicate on even higher levels of integration. These levels involve an object's incoming or outgoing interfaces (connectable objects), its persistent storage needs (the IPersist* interfaces), its formatted data structures (IDataObject), its display and printer renderings (IViewObject2), its individual late-bound methods and properties (IDispatch, OLE Automation, and property sets), its property pages (ISpecifyPropertyPages, IPropertyPage), its content appropriate for use in compound documents, and its ability to act as a custom control, which incorporates nearly every other OLE technology.

OLE is an incredible piece of technology, and it can be a very imposing one at that. At its core, OLE is elegant and simple, a generic set of mechanisms that improve the fundamental concepts of object-oriented programming. OLE also builds a great number of additional standards that open up a sometimes overwhelming number of possibilities. It has been our challenge to understand these possibilities and why they are important, looking at all the fine details that are necessary to make OLE work. For again, OLE is a technology aimed at component integration, and with potentially thousands of components and hundreds of ways to integrate them, the number of possible combinations is mind-boggling. But this is not a crisis; this is a tremendous opportunity. OLE has solved so many of the technical problems that only one question remains, one that I believe will become an ever more important question. What creative new software solutions can you achieve with OLE?