Object Design: Classes and Interfaces
OO design pioneer Rebecca Wirfs-Brock introduces object design classes and interfaces and their roles in software design.
Wirfs-Brock is the lead author on the book Object Design: Roles, Responsibilities, and Collaborations and Object-Oriented Design. This is one in a series of short articles based on her work.
Eventually, an object expresses its responsibilities for knowing and doing for others in methods containing code. An interface describes the vocabulary used in the dialog between an object and its customers: "Shine my shoes. Give me my shoes. That'll be five bucks, please. Here's your receipt." The interface advertises the services and explains how to ask for them.
It is often important to know more than just what an interface declares. To use an object's services, the conditions under which a service can be invoked may be important. Or an important side effect may need to be revealed.
Consider a gear in a machine. The number of teeth and the spacing between the teeth defines the gear's interface. This determines whether a gear will fit into a machine. But what if we replace one gear with another, built from a different alloy than the other gears? This new gear fits the interface, but as the gears turn, it may tend to overheat or break because it has different stress load characteristics. Its interface says nothing about this real-world limitation.
The more we publish about the behavior of an object, the more likely it is that it will be used as its designer intended. From a client's viewpoint, an object is more than its interface:
An object implements interfaces and affects other objects.
So what about information hiding? We're not talking about exposing everything about an object, but only the services and terms that are of concern to the client. We purposely hide the workings of our object's machinery. An object is a semiautonomous member of the community, stating, "It's none of your business how I do my job, as long as I do it according to our agreement! I don't want customers peeking inside to see how I conduct my business." It is the implementation of the object, not what to expect from it, that should be hidden.
Only the designers of an object's inner machinery should care about how an object implements its responsibilities.
The term class is used, in mathematics and in everyday life, to describe a set of like things. It describes all of these elements in a general way but allows each instance of the class to vary in nonessential features. Whereas the class is abstract and conceptual, the instances are concrete, physical objects. The visual image that appears to us at the mere mention of a tree contains the essential features that enable us to recognize any of the instances of tree when we see one. We easily distinguish between a car and a truck when one vehicle adheres to one description or the other, sport utility vehicles aside.
This everyday notion of a class also applies to software objects. We build our applications from sets of like objects. But a software class has some features that are specific to the software world. An object-oriented programming language allows a programmer to describe objects using classes and to define their behaviors using methods. There are additional requirements of an object-oriented programming language, but these two are key. They provide us with all that we need to build an application from objects.
Unlike a mathematical class, a software class is not simply an abstraction. Like the instances that it describes, it is concrete. To see it, you don't have to conjure it from nothing because it is described on index cards, diagrammed with a design notation, and written in programming code. You can pick it up, turn it over, read its description. It is an object. The features that we give the class are the features that we desire in its instances. Every responsibility for "knowing," "doing," or "deciding" that we assign to its instances becomes concrete in the class definition and the instance methods that the class contains.
If a software class provides two distinct sets of services, usually to two different sorts of clients, the class is said to play two roles. First, it plays a role that has no real-world analog. During program execution, a class acts as a factory for manufacturing, or instantiating, the instances required by the program (see Figure 1). It populates the computer memory with physical, electromagnetic objects, and it binds these memory areas to sets of instructions that they are responsible for. Our design objects—the abstract machines, roles, and clusters of responsibility that we invent to satisfy our design requirements—become classes in program code.
Classes play two roles. First, they act as factories, instantiating instances and implementing responsibilities on their behalf. Second, they act as an independent provider, serving clients in their neighborhood.
A class holds the blueprints for building instances. By defining a set of instance methods, it declares the names of the behaviors that other client objects can use. When an instance responds to a request from a client, it performs the corresponding method scripted in its class. The details of how the instances perform a task are pinned down in the instance's class definition and in its collection of instance method definitions. By browsing the instance's class and its instance methods, you can see whether the instance performs its responsibilities alone or delegates portions of its task to other objects in its neighborhood.
Classes hold the "shape" of the objects that they make.