Encapsulation: the Traditional View and the New View
My object-oriented umbrella
In my classes on pattern-oriented design, I often ask my students, "Who has heard encapsulation defined as 'data hiding'?" Almost everyone raises his or her hand.
Then I proceed to tell a story about my umbrella. Keep in mind that I live in Seattle, which has a reputation for being wetter than it is, but is still a pretty wet place in the fall, winter, and spring. Here, umbrellas and hooded coats are personal friends!
Let me tell you about my great umbrella. It is large enough to get into! In fact, three or four other people can get in it with me. While we are in it, staying out of the rain, I can move it from one place to another. It has a stereo system to keep me entertained while I stay dry. Amazingly enough, it can also condition the air to make it warmer or colder. It is one cool umbrella.
My umbrella is convenient. It sits there waiting for me. It has wheels on it so that I do not have to carry it around. I don't even have to push it because it can propel itself. Sometimes, I will open the top of my umbrella to let in the sun. (Why I am using my umbrella when it is sunny outside is beyond me!)
In Seattle, there are hundreds of thousands of these umbrellas in all kinds of colors.
Most people call them cars.
But I think of mine as an umbrella because an umbrella is something you use to keep out of the rain. Many times, while I am waiting outside for someone to meet me, I sit in my "umbrella" to stay dry!
Definitions can be limitations
Of course, a car isn't really an umbrella. Yes, you can use it to say out of the rain, but that is too limited a view of a car. In the same way, encapsulation isn't just for hiding data. That is too limited a view of encapsulation. To think of it that way constrains my mind when I design.
How to think about encapsulation
Encapsulation should be thought of as "any kind of hiding." In other words, it can hide data. But it can also hide implementations, derived classes, or any number of things. Consider the diagram shown in Figure 8-1. You might recollect this diagram from Chapter 7, "The Adapter Pattern."
Figure 8-1 Adapting XXCircle with Circle.
Multiple levels of encapsulation
Figure 8-1 shows many kinds of encapsulation:
Encapsulation of dataThe data in Point, Line, Square, and Circle are hidden from everything else.
Encapsulation of methodsFor example, Circle's setLocation.
Encapsulation of subclassesClients of Shape do not see Points, Lines, Squares, or Circles.
Encapsulation of other objectsNothing but Circle is aware of xxCircle.
One type of encapsulation is thus achieved when there is an abstract class that behaves polymorphically without the client of the abstract class knowing what kind of derived class actually is present. Furthermore, adapting interfaces hides what is behind the adapting object.
The advantage of this new definition
The advantage of looking at encapsulation this way is that it gives me a better way to split up (decompose) my programs. The encapsulating layers become the interfaces I design to. By encapsulating different kinds of Shapes, I can add new ones without changing any of the client programs using them. By encapsulating XXCircle behind Circle, I can change this implementation in the future if I choose to or need to.
Inheritance as a concept versus inheritance for reuse
When the object-oriented paradigm was first presented, reuse of classes was touted as being one of its big benefits. This reuse was usually achieved by creating classes and then deriving new classes based on these base classes. Hence the term specialized classes for those subclasses that were derived from other classes (which were called generalized classes).
I am not arguing with the accuracy of this, rather I am proposing what I believe to be a more powerful way of using inheritance. In the example above, I can do my design based on different special types of Shapes (that is, Points, Lines, Squares and Circles). However, this will probably not have me hide these special cases when I think about using ShapesI will probably take advantage of the knowledge of these concrete classes.
If, however, I think about Shapes as a way of classifying Points, Lines, Squares and Circles, I can more easily think about them as a whole. This will make it more likely I will design to an interface (Shapes). It also means if I get a new Shape, I will be less likely to have designed myself into a difficult maintenance position (because no client object knows what kind of Shape it is dealing with anyway).