Encapsulation is a language construct that facilitates the bundling of data with the methods operating on that data.
Information hiding is a design principle that strives to shield client classes from the internal workings of a class. Encapsulation facilitates, but does not guarantee, information hiding. Smearing the two into one concept prevents a clear understanding of either.
Encapsulation rule 1: Place data and the operations that perform on that data in the same class
This standard practice creates classes that adhere to the principles of abstract data types.
But you want more of your objects; you want them to represent cohesive, workable entities. A second rule concerns the manner of choosing the data and operations to encapsulate:
Encapsulation rule 2: Use responsibility-driven design to determine the grouping of data and operations into classes
This second encapsulation rule actually pertains to information hiding as well: don’t just bundle data and operations together; let design principles guide you. Ask yourself what actions an object of the class will be responsible for. Don’t let the class encapsulate more or less than a comprehensive set of reasonable responsibilities.
As you have seen from the many examples above, the Java language encapsulation facility isn’t enough to ensure a solid class design. The principle of information hiding stipulates that you shield an object’s clients from the internal design decisions made for that class of objects. So as a first rule for information hiding:
Information hiding rule 1: Don’t expose data items
Make all data items
private and use getters and setters. Don’t fool yourself into believing no harm will result from directly accessing an object’s internal data items. Even if only you code against those internals, future vulnerability still exists. You can’t predict when you might need to change the internal data’s nature, and brittle coupling with client objects sounds unnerving when shattered.
Go one step further when hiding design decisions concerning internal data. When possible, don’t even reveal whether an attribute is stored or derived. Client objects don’t need to know the difference. An attribute is a quality or characteristic inherent in a class of objects. From the client’s perspective, object attributes correspond to responsibilities, not internal data structure. That brings us to the next rule:
Information hiding rule 2: Don’t expose the difference between stored data and derived data
For example, a client object only needs to know that an object has an attribute of speed. Use an accessor method named
getSpeed() rather than
calculateSpeed(). Whether the value is stored or derived is a design decision best kept hidden.
As a corollary to the first two information hiding rules, I place all internal data at the bottom of the class text, after the methods that operate on the data. When I examine a class to understand its code, looking first at its internal data leads me to ask the wrong initial questions. I strive to understand the responsibilities of a class before concerning myself with any data structure details. Placing data after methods in the class text reminds me, every time I look at the code, to think first about a class’s behavior, not its structure.
The next rule concerns the choice of internal structure:
Information hiding rule 3: Don’t expose a class’s internal structure
Clients should remain isolated from the design decisions driving the selection of internal class structure. For example, a client should not know whether a primitive array or an
ArrayList is used to maintain an internal collection of objects. Internal structure is particularly apparent through the use of method names like
The final rule concerns choices of implementation details:
Information hiding rule 4: Don’t expose implementation details of a class
Don’t allow clients to know or invisibly affect a class’s implementation details. For example, a client should not be able to alter an internal calculation’s result by changing the state of objects used in that supposedly hidden calculation.
Though not an exhaustive list, those rules help separate the concept of encapsulation provided by the language from the information hiding provided by design decisions. Each rule is fairly easy to follow, and each will assist in creating classes that are not only more resilient to change, but also more easily used by client objects.