3 Encapsulation and Information Hiding
A progressive guide to using encapsulation, access control, properties, invariants, and information hiding to design safer and more maintainable object-oriented classes.
Foundations of
combines two related decisions: where an object’s state and behavior belong, and which parts of that object other code may use. A class can keep data together with the methods that operate on it, then expose only operations that preserve the object’s rules.
For example, a bank account should not let arbitrary callers assign a balance. It can keep the balance private and provide deposit, withdraw, and getBalance operations. Each operation can reject invalid amounts and update the balance in a controlled way.
This design protects state, centralizes rules, hides complexity, reduces dependencies, and allows the internal representation to change without requiring changes to client code.
Takeaway: is not merely putting fields and methods in the same class; it is controlling valid interaction with the object.
and Mutability
determines which code can depend on a type or member. public members are available to permitted client code, private members are available only within their declaring type, and protected members are available within the declaring type and, depending on the language, derived types. Some languages also provide package-, module-, or assembly-level access.
A strong default is to choose the most restrictive access level that still supports the design. A private field prevents callers from changing representation directly. A public operation can then validate inputs before changing the field.
A public mutable field is weakly encapsulated because any caller can bypass validation. Even a private field with an unrestricted public getter and setter may expose too much of the representation. Ask whether a value should be readable, replaceable, or changed only through a domain operation.
Takeaway: Restrict access first, then expose only the interactions that clients genuinely need.
Properties as Controlled Access
A provides field-like access while retaining the ability to execute logic during reading or writing. It may be read-only, write-only, or read-write, and its accessors can have different visibility.
A class such as User can expose a public Email getter while keeping the setter private. The setter can reject missing input and trim surrounding whitespace before storing the result. Client code can read the normalized email but cannot replace it arbitrarily.
Properties can also compute values. A FullName may combine a first name and last name without storing a separate full-name field. However, replacing every field with an unrestricted getter and setter does not automatically improve . If a value must not change after construction, expose only a getter or use an initialization-only mechanism when the language supports one.
Takeaway: Use properties to control access and behavior, not simply to provide automatic public assignment.
Invariants and Validation
An is a rule that must be true for every valid object. Examples include an account balance that cannot be negative, rectangle dimensions that must be positive, a start date that cannot follow an end date, and an order that cannot be shipped before payment.
Validate data at the boundary where it enters or changes the object: a constructor, setter, factory method, or domain operation. For a rectangle, the constructor can require width and height to be positive before storing them. Once those fields are private and every update path validates them, the area method can rely on the instead of repeating defensive checks.
Failure should be communicated clearly through an exception, an error result, a rejected operation, or another appropriate validation mechanism. Invalid state should not be silently accepted.
In a thermostat, for example, the target temperature can be readable publicly but assignable only through controlled operations. If the permitted range is from to degrees Celsius, the validation rule belongs in the object’s controlled update path. Operations such as Increase and Decrease can then preserve the same rule.
Takeaway: Put each important rule in the object responsible for maintaining it, and validate every path that can change the relevant state.
and Public Interfaces
focuses on which design decisions should remain concealed from clients. A playlist can expose addSong, removeSong, and playNext without revealing whether its songs are stored in a list, queue, or database. Clients depend on behavior rather than representation.
A clear should:
Express the object’s responsibility through meaningful operations such as
withdraw,schedule, orapprove.Expose behavior rather than a mutable internal collection.
Use names that reveal intent without requiring knowledge of internal algorithms.
Make invalid states difficult to create through validation.
Minimize the public surface because every public member can become a long-term dependency.
Control mutability with read-only properties, private setters, immutable values, or command methods.
Document valid inputs, results, side effects, failure conditions, and repeatability.
Return suitable abstractions such as a read-only view instead of an internal mutable collection.
Good lowers . If a class later replaces an array with a hash table, changes a sorting algorithm, or moves data to an external service, clients should continue using the same stable operations.
Takeaway: Expose stable responsibilities and observable behavior; conceal algorithms, storage choices, and other implementation details.