To this point, objects have been introduced as units that embody higher-level abstractions and as coherent role-players within an application. However, they couldn’t be used this way without the support of various language mechanisms. Two of the most important mechanisms are: encapsulation and polymorphism .
Encapsulation keeps the implementation of an object out of its interface, and polymorphism results from giving each class its own name space. The following sections discuss each of these mechanisms in turn.
To design effectively at any level of abstraction, you need to be able to leave details of implementation behind and think in terms of units that group those details under a common interface. For a programming unit to be truly effective, the barrier between interface and implementation must be absolute. The interface must encapsulate the implementation—hide it from other parts of the program. Encapsulation protects an implementation from unintended actions and inadvertent access.
In C, a function is clearly encapsulated; its implementation is inaccessible to other parts of the program and protected from whatever actions might be taken outside the body of the function. Method implementations are similarly encapsulated, but, more importantly, so are an object’s instance variables. They’re hidden inside the object and invisible outside it. The encapsulation of instance variables is sometimes also called information hiding .
It might seem, at first, that hiding the information in instance variables would constrain your freedom as a programmer. Actually, it gives you more room to act and frees you from constraints that might otherwise be imposed. If any part of an object’s implementation could leak out and become accessible or a concern to other parts of the program, it would tie the hands both of the object’s implementor and of those who would use the object. Neither could make modifications without first checking with the other.
Suppose, for example, that you’re interested in the Faucet object being developed for the program that models water use and you want to incorporate it in another program you’re writing. Once the interface to the object is decided, you don’t have to be concerned as others work on it, fix bugs, and find better ways to implement it. You get the benefit of these improvements, but none of them affects what you do in your program. Because you depend solely on the interface, nothing they do can break your code. Your program is insulated from the object’s implementation.
Moreover, although those implementing the Faucet object would be interested in how you use the class and might try to make sure it meets your needs, they don’t have to be concerned with the way you write your code. Nothing you do can touch the implementation of the object or limit their freedom to make implementation changes in future releases. The implementation is insulated from anything that you or other users of the object might do.
The ability of different objects to respond, each in its own way, to identical messages is called polymorphism .
Polymorphism results from the fact that every class lives in its own name space. The names assigned within a class definition don’t conflict with names assigned anywhere outside it. This is true both of the instance variables in an object’s data structure and of the object’s methods:
Just as the fields of a C structure are in a protected namespace, so are an object’s instance variables.
Method names are also protected. Unlike the names of C functions, method names aren’t global symbols. The name of a method in one class can’t conflict with method names in other classes; two very different classes could implement identically named methods.
Method names are part of an object’s interface. When a message is sent requesting an object to do something, the message names the method the object should perform. Because different objects can have methods with the same name, the meaning of a message must be understood relative to the particular object that receives the message. The same message sent to two different objects could invoke two distinct methods.
The main benefit of polymorphism is that it simplifies the programming interface. It permits conventions to be established that can be reused in class after class. Instead of inventing a new name for each new function you add to a program, the same names can be reused. The programming interface can be described as a set of abstract behaviors, quite apart from the classes that implement them.
Overloading:
The
terms “polymorphism” and “argument overloading” refer basically to the same thing, but from slightly different points of view. Polymorphism takes a pluralistic point of view and notes that several classes can each have a method with the same name. Argument overloading takes the point of the view of the method name and notes that it can have different effects depending on the arguments passed to it. Operator overloading is similar. It refers to the ability to turn operators of the language (such as ==
and +
in C) into methods that can be assigned particular meanings for particular kinds of objects. Objective-C implements polymorphism of method names, but not argument or operator overloading.
For example, suppose you want to report the amount of water used by an Appliance object over a given period of time. Instead of defining an amountConsumed
method for the Appliance class, an amountDispensedAtFaucet
method for a Faucet class, and a cumulativeUsage
method for a Building class, you can simply define a waterUsed
method for each class. This consolidation reduces the number of methods used for what is conceptually the same operation.
Polymorphism also permits code to be isolated in the methods of different objects rather than be gathered in a single function that enumerates all the possible cases. This makes the code you write more extensible and reusable. When a new case comes along, you don’t have to reimplement existing code; you need only to add a new class with a new method, leaving the code that’s already written alone.
For example, suppose you have code that sends a draw
message to an object. Depending on the receiver, the message might produce one of two possible images. When you want to add a third case, you don’t have to change the message or alter existing code, but merely allow another object to be assigned as the message receiver.
Last updated: 2007-12-11