Skip to content

Encapsulation

One of the most important principles of object-oriented programming is encapsulation. An object is a "black box". The contents of the "black box" should be a mystery. The object is used by sending it messages. These messages are part of the object's public interface. As with any "black box", the simpler and more elegant the interface, the better.

A television set is an excellent example of an object. It is a box with a very limited public interface (power on, channel up/down, volume up/down). Anyone can use this interface. It also has a protected interface, which is usually hidden behind some kind of panel. This allows the more experienced user to perform more complicated tasks (adjust the color, horizontal tracking, etc.). It also allows the user to mess things up in a way they never could using the public interface. However, the user cannot break the television using the protected interface. Finally, the television has a private interface. By removing the back panel of the set, you have access to all components inside the "black box". Note that the back panel has a big warning on it saying that there are no user-serviceable parts inside the set. You enter at your own risk. You can now break the television set. Opening the back of the television is a serious breach of encapsulation.

Objects often contain child objects. The separate existence of these objects should not be apparent to users. A television object contains many sub-component objects. You should never directly access or even need to know about these objects. This brings us to one of the most important rules of encapsulation: an object may know only about its child objects. You never, under any circumstances, send messages to descendants of an object's child. In other words, you don't send messages to grandchild objects, great-grandchild objects, etc. Disobey this rule in your program, and you've thrown out one of the major advantages of object-oriented programming.

The usual question that is asked at this point is, "How does an object communicate with a grandchild object?" The answer is, "You will never need to." Your object should contain all the messages required to do what you need. In some cases, the message handler in the object will actually send a message to one of its child objects.

A television contains a power supply, which is completely hidden. In order for the television to work, the power supply must be switched on (which is also none of our business). The user of the television would not ask, "How do I switch on the power supply?" Instead, they would say, "How do I turn on the television?" When its "public" power button is pressed, the television sends a message to its child object, the power supply, switching on the power. As the user of the television object, you should not even know about the power supply.

While an object should only know about its child objects, it does need to know something about the world that it lives in. An object often requires services from other objects. This object could be its parent or some other ancestral object. When an object requires services of another object, the object is said to have a dependency on that object. A television has at least two dependencies: it needs to connect to electricity and it needs to connect to some kind of receiving device (cable, aerial, etc.). If these services are not provided, the television will not work.

When an object has an external dependency, it will ask its parent for help in providing the needed service. Sometimes a parent object can directly provide the service required. More often, the parent object will provide a link between the object and another of its child objects. A room object might contain three objects: the television, an electrical wall socket, and a television cable connector. A television will ask its parent, the room, to provide it with the location of the electrical and cable outlets. It can then connect to these outlets. In object-oriented terms, the television asks its parent for the object_id of the objects it depends on. As long as the room can provide these object_ids, the needed connections can be made. Sometimes the parent will be unable to satisfy a dependency and will have to ask its parent for help. This is delegation. As long as one of the television object's ancestors can provide us with an ID, the television will be able to satisfy its dependencies (our television object has a very long connecting cable).

The more external dependencies an object has, the less portable the object is. A television only has two dependencies. Because of this, our television object can be used in any of many room objects. The objects in the television have a much greater number of dependencies. Some of the objects may have been designed to specifically work for this television and might work in no other. We are not as concerned about the portability of these objects.

View objects are very similar to a television object. They have a limited number of very generalized external dependencies. They need to be able to connect to a menuBar object, an error object, and a help object. Views are very portable. They can be added to and removed from a program with little difficulty. The objects inside a view will be far less portable. The DEOs expect other DEOs and very specific DDOs to exist inside the view.

The difference in portability between a view object and its component objects represents a good fit for data-entry programming needs. We want our views to be quite independent. We are less concerned about object independence within a view. With extra programming effort, one could design the objects within a view to be quite independent. In most cases, this is simply not needed and therefore not worth the extra effort.

Next Topic

Delegation of Messages