Encapsulation in the Real World

Posted by Mark at April 14, 2005 10:24 AM

Encapsulation is one of the cornerstones of object oriented programming. With encapsulation we can represent or "abstract" an object in computer code. We can describe the object; it's features, capabilities, properties, what it can do and how its behavior can be modified, to describe the rules of a system or to mimic the behavior of a real-world object.

But does OOP actually encapsulate objects as we are familiar with objects in the real world? The OOP approach does a good job at "wrapping" a set of behaviors and properties while hiding internal details, yet it is in these hidden details that OOP falls down and fails to truly encapsulate an object. One of the founding rules of a class is that it hides its implementation. The internal workings of the class are private, and can be accessed through methods, without knowledge of how the object works on the inside. With this approach the programmer can work with the abstract idea of the object, minus the ugly details of implementation, yet ignorance is not always bliss. If we take encapsulation to mean that an object is fully enclosed, distinct from its surrounding environment, then we find that we don't have encapsulation in the current OOP model. Since the internals of the class are hidden, the class is free to refer to any number of other classes, without formally describing these dependencies. This "hard-wires" the class into its surrounding context and breaks from true encapsulation.

For example let's compare the OOP model to a real-world situation. Suppose I see a pen placed on a piece of paper on a desk. It's safe to assume that I can pick the pen up. I don't expect the pen to be permanently attached to the desk it's resting on. It goes without saying that the pen is fully encapsulated (by virtue of its physical existence in the world), meaning that when I pick up the pen, the desk stays where it is. The paper stays where it is. There is no hard connection between the paper, pen and desk, though the three may be used in conjunction. I can pick the pen up and put it in my pocket, walk out of the room, even leave the country. The pen still works. I can write on paper, but can also write on other surfaces. Indeed I can try to write (with varying degrees of success), on any flat surface I find. The pen doesn't care what the "type" of the surface is.

Now translate the pen into OOP. We make a class called Pen with a method: write(Surface). Simple enough, but is this really encapsulating a pen? For the pen to write I need to also create a Surface class to write on. This is still a simple enough class definition, and accurately describes what a pen can do. But let’s look at the internals of the class, at what the Pen does with other classes. Let's say the Pen can draw either freeform lines or can type letters in a given font. The Pen class refers to a Surface class, a FontMetrics class, a Font, a Texture (for stippling lines) and perhaps a 2DGeometry drawing class for generating paths. Now in the OOP environment, when I try to move the Pen out of its existing context and compile it somewhere else, I find that I'm missing 5 other classes that must be present for the pen to work. The pen is married to the Surface it is designed to write on. It cannot function without it. We don't have a Pen, we have a Pen+Surface.

Two Java features let us split the pen away from the context it's developed for. We can use the import statement to define dependencies on external libraries, for things like Graphics contexts, awt or swing, but import statements are broad and don't provide much clarity on how the pen refers to these external libraries. And while import statements define references to defined packages (ie. Font,FontMetrics, awt, etc.), it doesn't help us much with referring to our own custom types, such as the Surface and Texture classes.

For our custom class types we can use interfaces to separate the Pen from dependent classes. For instance we can create a Drawable interface so that Pen.write(Surface) method becomes Pen.write(Drawable someDrawable). Now the pen is no longer married to the Surface class, it's married to the Drawable interface, and the Surface is also married to the Drawable interface. While the idea of a remote marriage has its appeals, it's still a fairly context specific arrangement. Other surfaces/canvases/panels must implement Drawable in order to be used by my pen. And we still haven't addressed the other references that the pen makes, such as the hypothetical Texture class. Do we create an interface for every external reference the Pen makes? Programmers are urged to take this approach through "best practices" but they usually don't. Despite the moral and ethical urgings of the gurus, this approach is time consuming, requires planning and a high level of agreement between the code-sharing parties. Ultimately it's not as flexible as we'd like. It doesn't pay off in the short run and the additional interfaces present a maintenance overhead in the long run, so this step is often skipped, and programmers fall back on the easy, fast and simple direct reference. It's easier for the pen to stay married to the paper than to arrange the (appealing, yet idealistic) threesome of paper pen and interface.

So does OOP give us a truly encapsulated pen? No not really. It's more like those pens at banks that are wired to the desk so you don't steal them. As long as it's easier to hard code references than to make soft references, hard references will propagate and classes will not truly be separable. A truly encapsulated pen is a Pen that is easy to steal, so easy that we have no need to create our own.

I haven't discussed how true encapsulation could work in OOP... I'll save that for another post.

 



Trackbacks

Trackback for this post: http://www.culturekitchen.com/cgi-bin/movabletype/mt-tb.cgi/2888



Comments



Post a comment











Remember personal info?



If you would like an answer personally addressed, please leave a real email address. Emails are kept confidential and they will never be shared with third parties.