
Encapsulation is widely perceived as a key feature, or advantage, of object technology. It seems to me, however, that the focus on encapsulation has always been a little bit off base; rather, what's important--as I've explained in this column before, on more than one occasion--is just to make a clear distinction between types and representations. As my title this month indicates, in fact, I feel encapsulation per se is something of a red herring, and this month I'd like to try to explain why I feel this way.
You probably already have a pretty good idea of what encapsulation means, but let me take a moment to spell it out anyway. Basically, a data type is said to be encapsulated if instances of the type in question have no user-visible components. (I'm using instances here as a convenient, albeit sloppy, shorthand for values or variables.) For example, in their book Smalltalk-80: The Language and Its Implementation (Addison-Wesley, 1983), Adele Goldberg and David Robson say:
"An object consists of some private memory and a set of operators ... An object's public properties are the [operator specifications] that make up its interface ... An object's private properties are a set of [components] that make up its private memory." [italics added for emphasis].
Note: I've made it plain in previous installments that I prefer not to use terms like object too much myself, because they're fuzzy and often cause confusion. In what follows, I'll use such terminology only when quoting from other writers.
As the above passage indicates, Smalltalk users can operate on instances ("objects") of an encapsulated type solely by means of the operators that have been explicitly defined in connection with that type. For example, we might have a type CIRCLE, and we might be able to invoke operators that return the area--or the circumference, or the radius (and so on)--of any given circle. However, we couldn't legitimately say that circles have an area component, or a circumference component, or a radius component (and so on). One important consequence is that we don't know, nor do we need to know, how circles are represented inside the system; rather, that representation is visible solely to the code that implements the operators. In other words, the type is of interest to users--it's part of the model--while the representation is of interest to the implementation only.
Here's another quote that covers more or less the same ground as the previous one but goes into a little more detail. It comes from a tutorial on object databases by Stanley Zdonik and David Maier:
"Encapsulation [means each type has] a set of [operations and] a representation ... that is allocated for each of its instances. This representation is used to store the state of the object. Only the methods implementing operations for the objects are allowed to access the representation, thereby making it possible to change the representation without disturbing the rest of the system. Only the methods would need to be recoded."1
Observe that (as this quote effectively suggests) encapsulation can be regarded as the familiar notion of data independence in another guise. After all, if we do manage to distinguish type and representation properly, and if we also succeed in our goal of keeping the representation hidden, then we can change that representation as much as we like without having to change application programs. (We only have to change the code that implements the operations.)
Perhaps you can begin to see why I don't think the notion of encapsulation, as such, is all that significant. After all, it basically means we don't have to worry about what we shouldn't need to worry about--namely, physical representations (also known as actual or internal representations). In other words, encapsulation really is, as already claimed, just a logical consequence of the crucial distinction we already draw between type and representation. But, of course, there's more that can usefully be said on the subject--hence this month's column.
One last point on the definition of the term: While preparing this installment, I took the trouble to look up encapsulation in a number of books (nearly 20 of them, in fact) on object technology and related matters. It was a pretty dispiriting exercise, I can tell you, and I was very struck by the fact that I could nowhere find a precise definition of the concept. (The best explanations were in the Smalltalk book already quoted, and I think it's telling that that particular book doesn't seem to use the term encapsulation, as such, at all. Certainly it's not in the index.) Anyway, one thing I did discover was that some writers seem to think the concept refers specifically to the physical bundling, or "packaging," of data representation definitions and operator definitions. For example:
"Encapsulation refers to the concept of including processing or behavior with the object instances defined by the class. Encapsulation allows code and data to be packaged together."2
But it seems to me that to interpret the term in this way is to mix model and implementation considerations. The user shouldn't care, and shouldn't need to care, whether or not code and data are "packaged together!" Thus, it's my belief that--from the user's point of view, at least, which is to say from the point of view of the model--encapsulation simply means what I said before: namely, that the data in question has no user-visible components and can be operated upon only by means of the pertinent operators.
As you might already know, the objective of encapsulation conflicts somewhat with the need to be able to perform ad hoc queries. After all, encapsulation means data can be accessed only via predefined operators, while an ad hoc query means, more or less by definition, that access is required in ways that can't have been predefined. For example, suppose we have a data type POINT; suppose we also have a (predefined) operator to "get"--that is, read or retrieve--the X coordinate of any given point, but no analogous operator to get the corresponding Y coordinate. Then even the following simple querie, and many others like them, obviously can't be handled:
Get the Y coordinate of point P
Get all points on the X axis
Get all points with Y coordinate less than five.
In fact, they can't even be stated.
In The Third Manifesto,3 Hugh Darwen and I deal with the foregoing conflict by requiring that, for any given type, operators be defined that expose some possible representation for instances of that type (we call those operators "THE_ operators," for reasons that don't concern us here). In the case of type POINT, for example, we might define operators THE_X and THE_Y, thus allowing operations such as the following:
Q := THE_Y ( P ) ; /* get Y coordinate of point P and assign it to Q */ Z := SQRT ( THE_X ( P ) ** 2 + THE_Y ( P ) ** 2 ) ; /* get distance of point P from the origin and assign it to Z */
(and so on). Thus, THE_X and THE_Y effectively expose a possible representation--namely, Cartesian coordinates X and Y--for points, thereby making it possible to perform ad hoc queries involving points. Note carefully, however, that this fact doesn't mean that points are actually represented by Cartesian coordinates inside the system; it merely means, to repeat, that Cartesian coordinates are a possible representation. The actual representation might be Cartesian coordinates, or it might be polar coordinates R and U, or it might be something else entirely. In other words, THE_ operators don't violate encapsulation, and they don't undermine data independence.
As an aside, I remark that DATE and TIME in SQL serve as examples of built-in types for which certain possible representations are effectively exposed. For example, dates have an exposed possible representation consisting of a YEAR component, a MONTH component, and a DAY component. Though I should perhaps add that those "possible" representations in SQL are unfortunately rather close to the actual ones; SQL is, regrettably, frequently unclear on the distinction between types and representations.
Another reason why I don't think encapsulation as such is all that important has to do with a point over which there seems to be much confusion in the literature, and that's as follows: Some types are definitely not encapsulated anyway, nor do we want them to be! I refer in particular to certain "generated" types--types, that is, that have been defined by means of certain type generators, such as ARRAY, LIST, TUPLE, and RELATION. To fix our ideas, let's focus on RELATION as a familiar example. (Although remarks analogous to those that follow apply to ARRAY and the rest as well, of course.) Suppose that, instead of the POINT type discussed in the previous section, we had a POINT relvar (relation variable) instead, defined as follows:
VAR POINT ... RELATION { X ... , Y ... } ... ;
/* types of attributes X and Y omitted for simplicity */
Note: The definition is expressed in Tutorial D, a language defined as a vehicle for illustrating features of The Third Manifesto. The ellipses (...) stand for certain aspects of the definition that aren't relevant to the present discussion and have therefore been omitted.
Now, this relvar definition makes use of the RELATION type generator to specify the (generated) type of the relvar, which is, of course, a specific relation type--namely, the relation type:
RELATION { X ... , Y ... }
/* again, types of attributes X and Y omitted for simplicity */
And this type is certainly not encapsulated--it has user-visible components, the attributes X and Y. And, of course, it's precisely the fact that it does have those user-visible components that makes it possible for us to perform ad hoc queries on relvar POINT; for example, we can project it over attribute Y, or restrict it to just those tuples with Y value less than five.
I note in passing that Mike Stonebraker makes some similar observations in his book on object/relational DBMSs: "Base types are completely encapsulated. The only way to manipulate [an instance of] a base type is to retrieve it or execute a function that takes [an instance of that type] as an argument. In contrast, composite types are completely transparent. You can see all the fields, and they are readily available in the query language. Of course, an intermediate position is to allow some fields of a composite object to be public (visible) and the remainder to be private (encapsulated). This is the approach used in C++."4 I should add, however, that it's not clear whether Stonebraker properly distinguishes, as The Third Manifesto does, between actual and possible representations.
Anyway, to get back to my main argument: Please observe very carefully that the fact that relation types aren't encapsulated doesn't mean we lose data independence! In the case of the POINT relvar, for instance, there's absolutely no reason why that relvar can't be represented physically by polar coordinates R and U instead of Cartesian coordinates X and Y. (I know they probably can't be represented that way in today's SQL products, but that's a defect of those products. As I've had occasion to complain in this column before today's SQL products provide very much less data independence than relational technology is theoretically capable of.) In other words, we still have to make a clear distinction between types and representations, even with nonencapsulated types like the relation type illustrated in the example, and "breaking encapsulation" is not necessarily the same thing as undermining data independence.
In The Third Manifesto, we require support for both a TUPLE and a RELATION type generator; as a consequence, users can define their own tuple and relation types. We also require users to be able to define types that, unlike tuple and relation types, are just "simple" types--that is, types like POINT, LENGTH, AREA, LINE, and so on--possibly even types like INTEGER, if the system doesn't provide them as built-in types. And we opted for the term scalar types as a generic way of referring to such "simple" types (and then we naturally talked in terms of scalar values and scalar variables and scalar operators as well).
Now, the reason we chose the term scalar was because:
It was already available. (It's been used with the meaning we had in mind for many years in the programming languages world.)
It did seem to be the obviously correct term to contrast with ones such as tuple and relation (and array and list and all the rest). In fact, I would argue that it's obviously correct even though the physical representation of those "scalar" values and variables can be as complicated as you like. For example, a given scalar value might have a physical representation consisting of an array of stacks of lists of character strings, in appropriate circumstances. (Yet again I stress the importance of not confusing types and representations.)
And I now observe that our term scalar means exactly the same thing as encapsulated--that is, a type is encapsulated if and only if it is scalar, in the foregoing sense. I therefore feel that if the industry had opted, as we did in The Third Manifesto, for the already available term scalar, there would have been no need to invent the term encapsulated at all. And I further feel that we might thereby have avoided some of the confusions I've been talking about in my column this month.
Note: Because they have no user-visible components, scalar (or if you prefer, encapsulated) types might be said to be atomic. I would rather not use such terminology, however, because it's led to too much misunderstanding in the past (on my own part as much as anyone else's, I hasten to add); instead, I prefer to concentrate on our "new and improved" understanding of the true nature of first normal form (another matter I've had occasion to discuss in this series before.
I've tried to show why, in my opinion, the term encapsulated is more trouble than it's worth. To summarize:
First of all, the term scalar seems (at least to me) to capture the essential idea better, and it has a much more respectable pedigree than encapsulated does.
Second, there seems to be a widespread misunderstanding to the effect that all data should be encapsulated; I've tried to show that this idea is mistaken, and again I think that to talk in terms of scalar vs. nonscalar types makes the true state of affairs much clearer and reduces the risk of confusion.
Third, some people seem to think of "encapsulation" as a physical rather than a logical concept anyway.
I therefore think it's worth trying to avoid the term encapsulation altogether--and I propose to take my own advice in this regard in my writings from this point forward.
1. Zdonik, S. B. and D. Maier. "Fundamentals of Object-Oriented Databases." Readings in Object-Oriented Database Systems. Morgan Kaufmann, 1990.
2. Barry, D. K. The Object Database Handbook: How to Select, Implement, and Use Object-Oriented Databases. John Wiley & Sons, 1996.
3. Date, C. J. and H. Darwen. Foundation for Object/Relational Databases: The Third Manifesto. Addison-Wesley, 1998.
4. Stonebraker, M. (with D. Moore).
Object-Relational DBMSs: The Next Great Wave. Morgan Kaufmann, 1996.
C. J. Date is an independent author, lecturer, researcher, and consultant,
specializing in relational database systems. His most recent books are Foundation for
Object/Relational Databases: The Third Manifesto, coauthored with Hugh Darwen, and
Relational Database Writings 1994-1997, both published by Addison-Wesley in 1998.
Correspondence may be sent to him in care of Database Programming & Design, 411 Borel Ave., Ste.
100, San Mateo, CA 94402.