In the previous chapters, we studied:
Objects
Classes
State
Behavior
Constructors
Object Lifecycle
OOP Pillars
RelationshipsAmong the four pillars of OOP, we introduced encapsulation as:
Bundling data and behavior together while controlling access to internal state.
Many developers stop there.
They think:
private:means:
Encapsulation AchievedUnfortunately, this is one of the biggest misconceptions in software engineering.
Consider this class:
class BankAccount
{
private:
double balance;
public:
void setBalance(double value)
{
balance = value;
}
};The field is private.
But can we do this?
account.setBalance(-1000000);Yes.
The object's internal state can still become invalid.
So despite using private, we have not truly encapsulated anything.
The deeper purpose of encapsulation is:
Protecting the integrity of an object.
Historical Context
In early procedural systems:
Data
Functionswere often completely exposed.
Example:
struct Account
{
double balance;
};Any code anywhere could modify:
account.balanceLarge systems became difficult to maintain because:
Every module knew everything.
Every module modified everything.
Every module depended on everything.The Real Goal of Encapsulation
Many textbooks define encapsulation as:
Data + Behavior
While technically correct, that definition missed the most important point.
The true goal is: Protect Invariants
What Is an Invariants?
An invariant is:
A rule that must always remain true for an object.
Examples:
Bank Account
Balance >= 0User
Email must be validOrder
Total Price >= 0Library Book
Available Copies >= 0If invariants beak:
Object becomes invalid.
Invalid objects create system-wide bugs.
Example: Invalid State
Bad Design:
class User
{
public:
std::string email;
};Usage:
User user;
user.email = "";Object now contains invalid state.
Nothing prevents it.
Encapsulation Solution
class User
{
private:
std::string email;
public:
bool updateEmail(const std::string& value)
{
if(value.empty())
return false;
email = value;
return true;
}
};Now:
Object protects itself.
Encapsulation Creates Trust
Imagine:
User user;Question:
Can another developer accidently corrupt it?
If encapsulation is good:
MaybeProfessional software depends on trust.
Objects should guarantee their own correctness.
Information Hiding
Now we move beyond encapsulation.
Definition:
Information Hiding means:
Expose only what users need to know and hide anything else.
Why Information Hiding Exists
Consider a car.
Driver sees:
Steering Wheel
Brake
AcceleratorDriver does NOT see:
Fuel Injection Algorithms
Transmission Control Logic
ECU SoftwareThe car hides implementation complexity
Software should behave the same way.
Example
Bad API:
class EmailService
{
public:
void openSocket();
void performHandshake();
void authenticate();
void sendBytes();
};Users must understand internals.
Better:
class EmailService
{
public:
void sendEmail();
};Simple.
Everything else hidden.
Encapsulation vs Information Hiding
Many engineers confuse them.
They are related but different.
Encapsulation
Focus:
Protect StateQuestion:
Who can access data?Information Hiding
Focus:
Hide ComplexityQuestion:
What should users know?Comparison Table
| Encapsulation | Information Hiding |
|---|---|
| Protects object integrity | Hides implementation |
| Controls access | Controls knowledge |
| Focus on correctness | Focus on simplicity |
| Prevents corruption | Reduces complexity |
Example
class CoffeeMachine
{
public:
void makeCoffee();
private:
void heatWater();
void grindBeans();
void brew();
};Encapsulation:
Private methods protectedInformation Hiding:
User only sees makeCoffee().Both working together.
Access Modifiers
C++ provides mechanisms for encapsulation.
Public
Visible everywhere.
public:Use for:
Stable APIs
Business OperationsPrivate
Visible only inside class.
private:Use for:
State
Implementation DetaislProtected
Visible to derived classes.
protected:Use carefully.
Often overused.
Visual Model
+----------------------+
| Public API |
+----------------------+
Hidden
+----------------------+
| Private State |
| Private Logic |
+----------------------+Public interface.
Private implementation.
Designing Good Public APIs
One of the most important software engineering skills.
Bad:
class Order
{
public:
std::vector<Item> items;
double total;
bool paid;
};Everything exposed.
Good:
class Order
{
public:
void addItem();
void removeItem();
double getTotal() const;
void pay();
private:
std::vector<Item> items;
double total;
bool paid;
};Controlled access.
Tell, Don't Ask Principle
A powerful encapsulation principle.
Bad:
if(order.getStatus() == PENDING)
{
order.setStatus(SHIPPED);
}External code controls internals.
Better:
order.ship()Order manages itself.
Mental Model:
Tell object what to do.
Do not ask object for data
and perform its work externally.Data-Centric vs Behavior-Centric Design
Data-Centric
class User
{
public:
string name;
string email;
};Behavios lives elsewhere.
Behavior-Centric
class User
{
public:
void changeEmail();
bool login();
void logout();
};Behavior lives inside object.
This is more object-oriented.
Information Leakage
One of the most common design problems.
Example:
class BankAccount
{
public:
double getBalance()
{
return balance;
}
private:
double balance;
};This is acceptable.
But:
class BankAccount
{
public:
std::vector<Transaction>& getTransactions();
};Danger.
External code can modify internals.
Information hiding broken.
Safer Version
const std::vector<Transaction>& getTransaction() const;Read-only access.
Much safer.
The Getter/Setter Trap
Beginners often write:
getName()
setName()
getAge()
setAge()
getEmail()
setEmail()for every field.
Result:
class User
{
private:
string name;
int age;
};becomes:
class User
{
public:
string getName();
void setName();
int getAge();
void setAge();
};Effectively:
Everything still public.Encapsulation illusion.
Better Design
Ask:
What behavior belongs here?
Example:
user.changeEmail();
user.updateAddress();
user.deactivate();Behavior-focused APIs.
Law of Least Knowledge
Also known as:
Law of Demeter
Rule:
An object should know as little as possible about other objects.
Bad
customer.getOrder()
.getPayment()
.getCard()
.getExpiryDate();Known as:
Train WreckToo many dependencies.
Better
customer.getPaymentExpiry();Complexity hidden internally.
Encapsulation and Coupling
Good encapsulation reduces coupling.
Without encapsulation:
Many classes depend on internal details.Changing implementation becomes dangerous.
With encapsulation:
Only public interface matters.Example
Version 1:
class Cache
{
private:
std::unordered_map<int,int> data;
};Later:
class Cache
{
private:
std::map<int,int> data;
};Clients unaffected.
Why?
Because implementation was hidden.
Leave a comment
Your email address will not be published. Required fields are marked *


