Also in Programming With Cocoa:
Object-oriented programming is the cornerstone of modern software, including Apple's Mac OS X. The fundamental logical unit of object-oriented programming (OOP) is the class. Think of a class as a tiny sub-program on your computer that exists for one very specialized purpose. You build applications by assembling these classes in a useful way, and directing them to work in concert to accomplish something more complicated. Thus, the sum is greater than the parts.
Building classes to construct an application is very much like building a skyscraper. We know that successful skyscraper construction requires the efforts of many talented individuals. Specialists are needed to pour the concrete, install the electrical systems, fit the plumbing, coordinate the communications, and complete a host of other complicated tasks.
For every task, there's someone who has done it before and knows how to do it right. But you can't simply get a group of specialists together and hope that they spontaneously organize themselves to build a skyscraper. You need a project manager, a foreman, or someone to who has a clear understanding of the grand scheme to manage the workforce. The project manager sets everything in motion, telling the specialists exactly what to do. In the world of object-oriented programming, your application is the project supervisor.
Each class has a specialized purpose. Fortunately when programming in Cocoa, Apple provides you with an army of specialists. The classes of Cocoa are experts -- master craftsmen -- at things like working with arrays, strings, and numbers. They know how to draw buttons, sliders, and windows. Together they have the know-how to run a successful application. Your job is to organize and manage these guys in whatever way you need them to work. And in this way, the task of building a complicated application -- a skyscraper -- is made that much easier.
Don't worry, they're good workers who will do what they're told. You just have to know how to communicate with them and understand how they operate. In other words, you need to understand how classes are built so you know what they're capable of accomplishing. In this column, I will go through the main concepts of object-oriented programming so you can be a more skilled manager of your army of specialists.
I mentioned before that classes are the fundamental unit of OOP. Classes serve as a blueprint for creating any number of objects that actually do the work at runtime. Objects are runtime manifestations of a class that exist in a computer's memory.
Classes define in source code the data structures and any number of functions that can use or modify these data structures. The data structures are called instance variables, and the functions are called methods. In this way, objects are endowed with a state determined by the instance variables and a particular behavior defined by the methods.
I think Apple described the basic premise of OOP best in their book Object-Oriented Programming and The Objective-C Language: "The insight of object-oriented programming is to combine state and behavior -- data and operations on data -- in a high-level unit, and object, and to give it language support."
To understand the nature of objects, you must first understand how methods and data interact. Methods can be envisioned as wrapping around the data contained by an object, protecting it from the environment in which the object exists. In this way, the focus of programming shifts from thinking about data, to thinking about the various actions objects can perform.
For example, it's very rare that object instance variables would be directly accessible by another object. Rather, special methods called accessor methods allow other objects to access variable values by invoking these methods that return the value of the instance variables.
Objects communicate with each other by sending messages. A message is a command to some receiver object (or simply the receiver) to perform some action by invoking one of its methods. In this way, objects in a complicated application send messages back and forth and amongst each other -- messages to obtain data, perform actions, store data, or whatever.
You could think of an application as a network of objects that are constantly communicating with each other to serve the end purpose of the application. This metaphor is so powerful, in fact, that objects are often referred to as either servers or clients. Clients request services of server objects that provide the requested service.
One of the most important concepts of object-oriented programming is inheritance. The idea behind inheritance is that new classes are created by inheriting and adding to the behavior and characteristics of some pre-existing class (a parent class). The parent class is called a superclass and the new class is a subclass.
In this scheme, subclasses specialize the functionality of their parent classes by adding to and customizing the inherited behavior. For example, in The Application Kit, the NSButton class is a subclass of the NSControl class, and thus inherits all properties and functionality of a control as defined in NSControl.
Inheritance naturally leads to a large-scale organization of classes that is hierarchical, like the roots of a tree. If you follow this back far enough, you arrive at a common class which all other classes inherit -- a root object. The Foundation Framework contains the root class of all Cocoa classes. Appropriately, this class is called NSObject, and it defines the basic behavior that makes an object an object.
Inheritance is useful in many ways. It allows you to reuse code that has been tested and is known to work well, meaning there's less chance of bugs creeping into your code. Additionally, inheritance provides some unique design solutions found only in OOP.
If you have many classes that implement some common behavior, you can extract that into a superclass from which they all inherit. This makes maintenance easier since updating one class effectively updates the entire tree of subclasses below it.
For example, say you have a graphics program that works with circles, squares, and rectangles. As far as the drawing program is concerned, the classes controlling these shapes will share a great deal of code that does not relate to the actual shape we see on screen. This common code could then be extracted to a superclass called
Shape from which
Circle would inherit and customize to their needs. This is the way inheritance makes things easier for us.
Another use for inheritance is to declare a number of methods in a class that its child classes may implement in their own unique way. These methods may be empty or partially implemented, but their purpose is to inform us that subclasses of this class need to implement these methods in their own way. This is known as a protocol.
Objective-C provides ways to create more formal protocols which are not associated with any particular class. As before, these formal protocols provide a set of method declarations that must be defined by classes that conform to this protocol. Cocoa makes extensive use of protocols, and these protocols will be discussed in their own right in later columns.
Encapsulation essentially means that any information regarding the implementation of a class is hidden from the user of a class. The implementation is wrapped -- encapsulated -- by the interface and the split between the two is absolute. This serves many purposes.
For one, code is more easily maintained. The interface of a class is essentially a contract between the class developer and the application developer using the class. The class developer is setting out in clear terms how the class will behave, and what methods are available. When you, the application developer, program using the class, you adhere to the interface. The net effect of this is that the class developer can freely maintain, or upgrade the class implementation without worrying about breaking anyone's applications -- as long as he too adheres to the interface he provided.
A classic example of the separation of interface and implementation are telephones. For years now, the telephone has had the same basic interface -- a handset and dial with 12 buttons. However, the phone companies have always upgraded their systems making them faster or more reliable or whatever they do. But the point is: The interface never changes. When phone networks went digital you didn't have to relearn how to use a phone. The same idea is at work with classes. You learn them once and the implementers can go about their business. It really is a wonderful way of running things.
Before I finish, I'd like to talk to you about a subtle effect of the way objects and object-oriented languages are built that gives this technology much of its power. It's called polymorphism. What this means is that many classes can -- and will -- contain and implement methods with the same name.
Clearly one of the benefits of polymorphism is that you, the developer, have a smaller vocabulary of methods to remember to access core functionality. For example, in the Application Kit, many classes implement methods such as
floatValue which return numerical values when invoked.
Since many objects implement methods by the same name, it's possible to dynamically select message receivers at runtime. Let's go back to our hypothetical graphics program to illustrate this. Like any modern application, we want to implement cutting and pasting. So whenever the "cut" command is chosen, a "cut" message is sent to the selected objects on screen causing them to be stored on the clipboard.
The key word here is "selected." Obviously you can't hard-code the message before compilation to send a cut message to an object in your drawing. Rather, the receiver of the "cut" message sent by the "cut" command is selected beforehand by clicking and selecting an object on screen. It is dynamically created at runtime in response to the users actions! For this to work, however, every object that could potentially appear onscreen as a part of your drawing must implement a customized version of the "cut" method.
In the next column, I will tell you about how Objective-C implements the concepts I've covered today, as well as showing you what Objective-C code actually looks like. Don't miss it!
Michael Beam is a software engineer in the energy industry specializing in seismic application development on Linux with C++ and Qt. He lives in Houston, Texas with his wife and son.
Read more Programming With Cocoa columns.
Return to the Mac DevCenter.
Copyright © 2009 O'Reilly Media, Inc.