eyt*
Aug 21, 2004

Where are my interfaces???

As many of you are probably aware, the C++ Standard is presently being revised, lovingly referred to as C++0x; Herb Sutter has mentioned the topic a few times during the last two years in CUJ. One topic that I haven't seen expressively described for consideration is that of interfaces. While C++ does not explicitly define syntax for interfaces, you can generally implement interfaces via abstract base classes, but as Peter Coad warns in Java Design, “in C++, both concepts are expressed with a single syntax blurring the distinction between these very different mechanisms.”

What Peter is talking about is that interfaces are generally implemented in C++ as classes; these classes should only have methods defined, however, this is not enforced by the language. Of course, there are methods of implementing proper interfaces in C++, such as David Abrahams's template-based solution published in CUJ's September 2004 issue in an article by Christopher Diggins entitled “C++ with Interfaces.” But while this is a very interesting fashion to implement interfaces, it requires a substantial declaration that is not as convenient as a Java or IDL interface.

Conceptually, an interface defines the set of methods that a class implements; it does not define any member variables or a default implementation. From a design perspective, this provides maximum flexibility, as any object that implements that interface (the set of methods) can be utilized. Such design, when done properly, allows for extensibility.

Peter Coad's words are right, though; it is hard to tell to difference between an interface and an abstract class in C++, but it is equally hard to determine when a set of functions forms an interface and when that set of functions forms a class. While abstract classes provide a simple solution, interfaces provides a more flexible and extensible implementation. In the aforementioned book, Peter Coad provides us with a simple way to determine when to use classes and when to use interfaces; for each relationship between classes, Peter recommends that we ask whether that relationship is specific to those two classes, or if any object that implements a specific interface would be allowed to be used. While this advice is sound, I still believe that you really need to see interfaces in action before you can fully realize their power. Consider the following class design:

Communication class, with a derived SerialCommunication and CorbaCommunication class

In this class diagram, we see an abstract Communication class that defines a few abstract methods methods, and two derived classes, SerialCommunication and CorbaCommunication. As the SerialCommunication class is intended to communicate to something via a serial communications port, its mapping is a one-to-one mapping to the abstract class. But in the case of the CorbaCommunication class, this is not really as clear. While the server side of this class could correlate directly to a serial communication object directly, it does not necessarily have to; it could simply append and read from a file on disk.

In this particular case, a user of the Communication object does not require more an the above interface, and as such, an interface can provide more flexibility than a class.

But why is this and why am I bothering to talk about this? Well, later in the same September 2004 CUJ issue, Herb Sutter and Jim Hyslop talk about containers in their Conversions Column (#51 to be specific), and specifically mention that typedef std::list<int> ListType and iterators can be used to minimize the amount of code to change when changing the container type (in the article, they convert the container from a list to a vector). But why should we go through all this mess?

Each Java container class implements the java.util.Collection interface, in which changing a container is as simple as changing the assignment where the container is created. In C++, this is not that easy, even with the two above suggestions from Herb and Jim. For example, Item 44 of Effective STL reminds us to prefer the member functions to algorithms with similar names, or more specifically, if you have a std::set, you should prefer using the member function set.find() to std::find(). As such, when changing a container from a Set to a Vector, for example, you must go through the code to ensure that either you are using the most optimum code for that specific container, and you are using code that compiles. Both cases can be rather painstaking exercises. With Java, however, the Container interface ensures that I have a contains() method implemented that is the most probably the best choice for whichever container that I utilize (it should be noted that to be used in C++, it would require a templatized interface).

While they could have used an abstract base class to depict a container, the designers elected against it, for probably one the reasons that Chris mentions either in the aforementioned article or on his C++ Proposal for Interfaces. Some of the reasons that are mentioned are performance-related, and this probably had the most bearing on the design decision. Never the less, if the decision had been to create a common interface to all these classes, the entire issue could be moot.

While many object-oriented languages support interfaces (such as Java, SmallTalk, and C#), C++ does not yet enforce true interfaces, blurring the definition between an interface and an empty abstract class. While I have not heard anything about it, I hope that true interfaces will be part of the next C++ standard.

Filed In

Navigation

eyt*