|
Growth, Flexibility,
& COM
by Binh Ly
In COM, interfaces are the key to server flexibility and growth. An interface
has one important characteristic: Clients see an interface as simply a list of
methods, not how the methods are implemented. Because of this,
- A server object is free to implement the methods in however way it wants. More
importantly, an object that implements a method today can come
back a year later and re-implement that method in a different way. As an example of how this feature is useful: consider a
server object that's used to retrieve information from a mainframe database.
A year later, you decide to move all your database information from your
mainframe database to a desktop database. You now have to rewrite parts of
your server object to retrieve data from the desktop database instead of the
mainframe database. If, in all of this, you never change the server's
interface, the client would still work with the server as if the server
never changed. Since the interface is the client's view of the server and if the interface never changes, the client's
friendship with the server is never broken.
- Two (or more) objects can implement the same interface in different ways.
Consider the above example with a different twist: You only copy information
from the mainframe to the desktop because some clients might still want to
access the mainframe information at the same time other clients are
accessing desktop information (this example might seem rather contrived but
is a very common scenario in legacy migration projects). Given this, it's
very reasonable to create 1 server object that gets data from the mainframe
and another server object that gets data from the desktop database. These 2 objects
can expose the same interface so that to the client, there is absolutely no
difference in retrieving data from the mainframe or from the desktop. All
the client needs to do is create the correct server object and then use the
one interface common to both objects.
In many respects, an interface is a "contract" between the client
and the server. Once the server says "This is my interface and this is what
it looks like", the server is giving its word to clients that:
- It will always support and uphold the interface to the best of its
abilities. This means that a server can not just one day decide to not
support the interface anymore. If it did this while there are existing
clients that are dependent on that interface, the server would quickly make
a list of enemies.
- It will never change the interface. Change means removing a
method, changing a method, or switching the order of methods. Why is this
important? If a method is removed from an interface while existing clients
still expect to use that method, that's a sign of war. Same goes if the
server changes a method such as changing the method's parameters or if the
server reorders methods.
At first, not being able to change an interface seems to be a severe
limitation. What if we want to change some methods? What if we want to add new
methods? No problem! We simply create a newer interface and add it to the list
of interfaces that the server supports. Clients who want (and know about) the new functionality
simply query for the newer interface. At the same time, old-time clients who
have no interest nor knowledge in the new functionality can still use the same
object through the older interface. The important idea here is that servers can
continue to grow and evolve without leaving old clients behind - a key to
maintaining long-lasting friendships.
Evolution also sometimes requires that servers build themselves on top of other
servers. If a server wants to implement some functionality that is already
implemented in another server, why not just make friends with that other server?
A server can even build itself on top of several smaller servers not unlike a
house built on top of bricks and iron.
As an example let's consider our contact manager (C) and messaging (M)
applications from the previous
chapter. If you recall, C uses M's Messenger
object to send email and fax messages out to its contacts. Assume that we are
now interested in developing a Scheduler application (S) in which we want a feature where we
can send an email greeting to a contact on his birthday.
Since C has the ability to find out the birthdays of each contact and already
uses M to send messages out to contacts, why don't we just make C a server to S.
So C
is now a server built on top of another server (M).

Figure: Scheduler as a client of
Contact Manager; Contact Manager as a client of Messenger
If C exposes an object, ContactManager, we can define
and implement ContactManager's
interface as follows:
IContactManager = interface
procedure SendGreetingMessageToTodaysBirthdayCelebrants;
end;
procedure IContactManager.SendGreetingMessageToTodaysBirthdayCelebrants;
begin
Find all contacts whose birthday is today;
Create Messenger object from M;
For each contact whose birthday is today
Send birthday greeting to contact using Messenger's
IEmail.SendMail method;
end;
Client S would now be very happy to use the ContactManager object from
C:

Figure: Scheduler using the ContactManager object, which in turn uses
the Messenger object
Where Are We?
COM clearly distinguishes between an object's interface and implementation.
Interfaces rule when it comes to server flexibility, growth, and evolution. When
we dig more and more into COM, you'll find interfaces everywhere so before
taking the next step, it's very important that you understand everything I've
said here.
Further Reading
- Understanding ActiveX and OLE by David
Chappell
- Inside COM by Dale Rogerson
- Inside DCOM by the Eddons
- Essential COM by Don Box
|