Vous êtes sur la page 1sur 12

Chapter 1: Introduction to COM

This chapter provides an overview of current software development and describes why some of these techniques may be useful, but fall short of providing the necessary component communication, version maintenance, and location transparency. The Component Object Model (COM) addresses some of these constraints. At the end of this chapter, you will better understand how to use COM to develop component software. Although this material is presented from a C++ and Microsoft Windows perspective, the basic principles are true for the design of large, distributed, object-oriented systems.

Objectives
After completing this chapter, you will be able to: List and explain the problems that confront component software developers today. List and explain attempted solutions to traditional component software development. Explain the drawbacks of various solutions to component software development. List the solutions that COM provides for component software development problems.

Chapter 1: Introduction to COM

Traditional Software Development


Software designers and developers are faced with a number of challenges. Customers demand highquality software with large feature sets, but the competitive environments in which software development occurs are forcing increasingly shorter delivery times. In this section, you will look at the traditional approach to software development and its associated problems. This section includes the following topics: The Traditional Approach Problems with the Traditional Approach

The Traditional Approach


One solution to the challenges of software development is to separate the functionality of a large, monolithic application into smaller components. Traditionally, developers have used libraries of functions as a way of accessing the functionality of a component. These libraries implement functions through an application programming interface (API). Reuse of these functions is as simple as learning the semantics of the API and linking to the library. The following illustration shows how you can use a component from any vendor by calling it through a standardized API:

An example of a standardized API is the Microsoft Open Database Connectivity (ODBC) API. Many software vendors have implemented the ODBC API in their database drivers. Even with fairly widespread adoption of the ODBC API, interoperability is still challenging because implementation differences can occur from one vendor to another.

Notes

Chapter 1: Introduction to COM

Problems with the Traditional Approach


The traditional approach of using APIs to access the functionality of a software component has its drawbacks. These drawbacks include evolution of the API, versioning, component communication, and the implementation language.

Evolution of the API


The evolution of an API is a problem for both the API creator and software vendors who want to add value by extending an API. Any changes made to an API by its creator can potentially break existing applications. Changes made to extend the API can result in inconsistent implementations.

Versioning
Advertising and maintaining different versions of the API can also be problematic. As an API creator, how can you force a developer to check for the correct version?

Component Communication
Enabling components to communicate with each other is challenging, especially if different developers have created the components.

Implementation Language
The programming language you use for creating components greatly impacts how the components will communicate through an API. For example, if you create components in C++ and export classes from a library, it can be challenging to use C or Visual Basic to create the client of the component.

Problems of Traditional Software Development


Programmers have used a variety of object-oriented programming techniques to try to solve problems associated with traditional software development. In this section, you will examine the problems associated with using object-oriented programming techniques for developing components, and learn about a specific object-oriented programming solution to these problems. This section includes the following topics: Object-Oriented Techniques Limitations of Using C++ Example of Converting a Class to a Component Component Implementation

Notes

Chapter 1: Introduction to COM

Object-Oriented Techniques
One of the first attempts of developing components was to apply object-oriented techniques. Object-oriented analysis and design enables application designers to approach software development from a higher level of abstraction. Viewing an application as a collection of objects (which have attributes and behaviors) that interact with each other enables a software developer to more effectively model the problem and create a correct solution. An object design is also more comprehensible than a series of algorithms. Object-oriented design, on its own, is not sufficient to handle the complexity of modern software development.

Limitations of Using C++


When using C++ as the language for applying object-oriented techniques to your component development efforts, you should consider the following in-process and out-of-process server issues.

In-Process Server Issues


When developing an in-process server for component use, consider the following: When adding methods or data to a base class, the size of the derived class data or the VTBL layout changes. You have to recompile all classes below the base class. When deriving from a base class, there is no way to distinguish why or how that base class is being used. Perhaps the implementation is needed by the derived class. Perhaps the base class contains only pure virtual functions, in which case it's the interface of the base class, not its implementation, that is of importance to the derived class. C++ does not provide an easy way to make this distinction clear, and can result in the semantic integrity of the base class being corrupted.

Out-of-Process Server Issues


When developing an out-of-process server for component use, consider the following: As you debug and improve a component, you cannot guarantee the size of the C++ objects that constitute the component. A client may be dependent on a particular component size. The client of the class and the library implementing the class can thus become out of synch. Even if the size of the class has not changed, adding virtual functions to a class can cause the clients of newer versions of a class to stop if they use an older version of the class. In C++, encapsulation is by convention and cannot be enforced, so there isn't a good way to guarantee the integrity of a class. Bugs in an out-of-process server are almost impossible to track down.

Notes

Chapter 1: Introduction to COM

Example of Converting a Class to a Component


To illustrate how to rewrite a class for use as a component that was originally created with object-oriented techniques, consider this example of a string class: class String { public: short getLength(); private: char data[256]; } ; An instance of this version will either waste space, or be inadequate for all strings. You then might modify it like this: class String { public: short getLength(); private: char *data; short len; } ; Any clients that are dependent on the size of the class will be broken by this modification and would need to be recompiled. Besides changing its data members, there are other changes you might make to the class that can affect an object's size. For example, you might add a virtual function that translates the string into another language, like this: class String { public: virtual String Translate(short localeID); short getLength(); private: char *data; short len; } ; Now that the class has a VTBL, each object must have a VPTR, which adds four bytes to the size. Again, any clients that are dependent on a particular size will be broken. In another attempt to improve the string class, you can isolate clients from any modifications to data implementation. You can then move the data portion of the string class to its own class. Finally, you can add a pointer to that class, making it the sole data member of the string class. It will look like this: class String { public: virtual String Translate(short localeID); short getLength(); private: StringImpl *pImplData; } ;

Notes

Chapter 1: Introduction to COM

This version of the class is much better; however, it still gives clients an object with a specific size. With this version, you are close to providing a string class with only functions. In a final attempt at improving the string class, you make all functions pure virtual. You are simply publishing an interface for a string class that looks like this: class String { public: virtual String Translate(short localeID) = 0; virtual short getLength() = 0; } ; All of the virtual functions from our string class example are pure, making the class abstract. Currently, clients are not dependent on the size of an object or layout. A client will have only an interface pointer; the data representation is completely hidden. The creation of objects in this manner is a major concept of COM.

Component Implementation
To create an instance of an abstract class, you define a private implementation hierarchy whose classes are derived from the published abstract classes.

Creating an Instance of an Abstract Class


Because you do not want these implementation classes to be visible outside of the component, you can provide a function that creates instances of the derived classes, but returns a pointer to the base class, as shown in the following example code: class String // What the world sees { public: virtual String Translate(short localeID) = 0; virtual short getLength() = 0; }; class StringImp : public String // Inside the library { public: virtual String Translate(short localeID) {} virtual short getLength() {} }; String * CreateString() // Exported from the library { return new StringImp; }

Notes

Chapter 1: Introduction to COM

A client obtains a pointer to a String object by calling the CreateString function, as shown in the following example code: void ClientFunction() { String * pString; pString = CreateString(); short length = pString->getLength(); ... } The client never sees the StringImp class, nor does the client call the new operator. The client is aware only of the VTBL of the String interface.

Interface Evolution
This model for deriving from abstract classes can be used for multiple levels of derivation to change an interface without changing the base class. To add new functionality to an interface, you derive new classes from the abstract hierarchy. You can then use dynamic_cast to allow for safe casting up and down the derivation tree. The following example code shows how the String2 class can change an interface: class String2 : public String { public: virtual String Translate(short localeID) = 0; virtual short getLength() = 0; virtual void ConvertToUpperCase() = 0; }; class StringImp : public String2 { public: virtual String Translate(short localeID) {} virtual short getLength() {} virtual void ConvertToUpperCase() {} };

Notes

Chapter 1: Introduction to COM

An existing client can continue to use this new object because the client only knows about the methods of the String class, which are still supported. Using the dynamic_cast operator, a new client can determine if a method is supported, as shown in the following example code: void NewClientFunction() { String * pString; pString = CreateString(); short length = pString->getLength(); // Everything is the same as older client up to this point // Verify new server capabilities String2 *pString2; pString2 = dynamic_cast<String2 *> pString; if (NULL != pString2) pString2->ConvertToUpperCase(); ... } The new client also works with an older server. The new client determines whether the server supports the added interface before calling a method of that new interface. Ultimately, what you are doing is exporting tables of pointers to functions, or VTBLs.

Software Development with COM


COM consists of two key elements: a specification that defines a programming protocol, and a set of services that provides the functions for creating and exposing objects. In this section, you will learn how COM solves some of the problems associated with using object-oriented programming techniques when developing components. This section includes the following topics: COM Defined The Goals of COM COM and ActiveX

COM Defined
COM is a standard (or model) for the interaction of binary objects. An important feature of COM is that objects are precompiled, which means that the implementation language is irrelevant. If you include tokenized code (for example, the P-Code in Microsoft Visual Basic or the bytecode in Java), objects will not necessarily be tied to a specific hardware platform or operating system. COM is also an integration technology. Components can be developed independently, and COM provides the standard model for integrating these components. One can think of COM as an enabling technology, rather than a solution in itself.

Notes

Chapter 1: Introduction to COM

The Goals of COM


This topic introduces the major goals of COM. These goals are language and vendor independence, location transparency, and reduced version problems.

Language Independence
When developing components, you should not need to choose a specific language. In COM, any language can be used if it allows for calling functions through function pointers. Even interpreted languages are not excluded if the interpretive environment can provide these services on behalf of the application. You can therefore develop COM component software by using languages such as C++, Java, Visual Basic, and Visual Basic Scripting Edition (VBScript).

Location Transparency
In addition, you should not have to know in which module and location the file system provides a service. This becomes increasingly important when specific services cannot be provided locally, if services are late-bound, or if the process that provides these services changes location. Just as hard-coded paths are problematic in applications, hard-coded dependence on the location of services can also cause errors. COM separates clients from servers, which allows servers to be moved without impacting clients.

Vendor Independence
Consider an example of what happens frequently in the current model for software development. A new vendor provides an ODBC driver for your database that is better than the driver provided by your current vendor. It would be a lot of effort to port all existing code to use the new driver. The effort involved might tempt you to keep the existing, less effective driver. Because COM objects export only interfaces, any new object that exposes the same interfaces as an existing object can transparently replace the existing object. Vendor independence extends not just to external vendors, but also internally to objects that can be easily upgraded without recompiling.

Reduced Version Problems


COM requires immutable interfaces. Although this specification requirement does not entirely eliminate version problems, it greatly reduces the extent of the problem.

COM and ActiveX


The focus of Microsoft Object Linking and Embedding (OLE) 2.0 was to enable application integration at the compound-document level. For example, a user would be able to embed or link a spreadsheet into a word-processor document.

Notes

10

Chapter 1: Introduction to COM

OLE 2.0 was the first technology from Microsoft to be based on COM, as shown in the following illustration:

Since OLE was introduced, Microsoft has released a number of additional technologies based on COM. These technologies include OLE Automation and OLE Controls, as shown in this illustration:

However, the use of the term OLE in the names OLE Automation and OLE Controls was not quite accurate because these technologies had nothing to do with linking and embedding. In an attempt to resolve this confusion, Microsoft replaced the term OLE with the new term ActiveX. The only technologies that kept the name OLE were those that actually related to linking and embedding.

Notes

Chapter 1: Introduction to COM

11

The following illustration shows the difference between ActiveX and OLE, each of which is built on COM:

Notes

12

Chapter 1: Introduction to COM

Self-Check Questions
1. Which one of the following is a goal of COM? A. B. C. D. Enforcing development consistency by requiring all components to be developed in the same language. Enabling clients to be uncoupled from servers by not requiring components to know the physical location of other components. Tracking the version of each component so that only the correct and most recent version is used. Enabling component developers to choose either C++ or Visual Basic as their development language.

2. One of the problems with the traditional API-based approach to developing component software is the continuing evolution of the API. Which one of the following best describes this problem? A. B. C. D. The API cannot be extended to provide additional functionality. As the API evolves, the implementation language must not change. Any changes made in the API by its creator can potentially break existing applications that use the API. There are no problems with the traditional API-based approach to component software development.

Notes

Vous aimerez peut-être aussi