In other words, we need to know what methods the component exposes, the parameters that each method expects, and the return value from each method. In order to facilitate this, COM distinguishes the description of a component’s functionality from its internal workings.
- This description of the component’s functionality is defined by the component’s interface. A component can have many interfaces, but related methods are generally grouped together within the same interface. A lot of people in the COM world (including the authors of this book!) agree that interface-based programming is the single most important and powerful aspect of COM.
- The ‘internal workings’ of the methods and properties – that is, the code that allows them to perform their tasks – is generally referred to as the component’s implementation.
An interface is really nothing more than a list of methods, properties and events (you’ll meet the second and third of these terms in a moment). When we want to use the component, the interface tells us how to do it. The interface doesn’t give any details of the component’s implementation, but its existence implies a promise that the functionality it describes will always be available.
The distinction between interface and implementation is an important one. It’s the interface that provides the link between our applications and the component itself. We can replace an old component with a new one that has a different implementation, provided that the new component provides the same interface as the old one. Otherwise, applications designed to use the original interface may break.
To help us understand interfaces, let’s think about cars. We’re going to model a car in terms of a component. If you want to be really imaginative, you can pretend that you’re an ASP page (rather than a human being) driving the car. The car component provides functionality that can be defined by a number of interfaces. For example, the component has an interface that defines how you drive the car – we’ll call it IDrive (by convention, interface names often begin with I).
The IDrive interface has these methods:
The methods are the object’s way of allowing us to use it to perform a task. The methods above allow us to accelerate, brake, and steer the car. By using the object’s methods to perform the tasks, we don’t need to worry about what is going on inside the object. If we want to turn the car to the left, we don’t need to get under the car to find out how the steering mechanism works – we just sit in the driver’s seat and call its SteerLeft() method (equivalent to turning the steering wheel).
Some methods need additional information, and will adjust their behavior accordingly. For example, the SteerLeft method would need to be told just how far to turn the steering wheel to the left. To this end, such methods are capable of receiving parameters – values required to execute the method.
Properties are the settings or stored values that are contained within an object, some of which are exposed to the user. These values tell us about the appearance or behavior of the object – and we can change some properties too. The IDrive interface might include properties that tell us the temperature of the engine, and the amount of gas in the tank.
There are three types of properties: read-only properties, write-only properties, and read – write properties. For example, the car probably has a read-only property that tells us what mileage it has done. You can affect the values of some read-only properties indirectly – driving the car will increase the mileage – but you’re not permitted to set the mileage directly. The tripometer (which tells you how far you have gone) would be a read-write property – you can write to it by pressing the button that resets it to 0, and you can read it by looking at the display that tells you how far you traveled since you last pressed the button.
We should also briefly mention events. If methods are our way of telling an object what to do, events are the object’s way of telling us that something has happened. For example, many modern cars have a device that is capable of monitoring the amount of gas in the tank. If the gas level falls below a certain level, the device fires an event, which informs the object that the gas level is low. This allows the object to react to the event – in this case by displaying a bright-red warning sign on the dashboard. Reacting to an event in this way is called event handling.
Using the Interface
The nice thing about cars is that generally, once you know how to drive one, you know how to drive them all. All cars use a steering wheel to steer; you make them go faster by pressing the accelerator pedal, and slower by pressing the brake pedal. The same is true for trucks and juggernauts. How does this relate to components? Well, we can have lots of different types of component that all expose the same interface. The car component exposes the IDrive interface, but so do the truck component and the juggernaut component. Once we know the methods and properties exposed by the IDrive interface, we know roughly how to use that interface on any component that exposes it.
You will often see methods written with parentheses after them, like this:
Within the parentheses, you might need to include one or more other items – these are the parameters we mentioned earlier. The BrakeHard() method doesn’t have any parameters (when we ask the car to brake hard, it will stop quickly – the car doesn’t need any more information than that). By contrast, when we accelerate we’ll need to tell the car by how much we want to accelerate – and to do that we use a parameter like this:
This will work provided that the object knows what we mean by an acceleration unit of 3. This should be defined in the documentation for the component: “Insert an integer as the first parameter and I’ll convert that into miles per hour and increase the speed by that amount”. Again, as a driver we don’t mind how the car component achieves this acceleration – we just have the promise that it will.