OOP Pillars#
flowchart LR
E([Encapsulation<br/>state behind a barrier])
A([Abstraction<br/>essential, not incidental])
I([Inheritance<br/>is-a hierarchy])
P([Polymorphism<br/>one name, many forms])
E --> A --> I --> P
classDef p fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
class E,A,I,P p;
classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
classDef storage fill:#e5e7eb,stroke:#374151,stroke-width:1px,color:#0f172a;
classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
class E,A,I,P service;
Four foundational ideas every OO language gives you: Encapsulation, Abstraction, Inheritance, Polymorphism - usually shortened to "EAIP" or just "the four pillars".
1. Encapsulation - state behind a barrier#
Bundle data with the methods that act on it, hide the internals, expose only a contract.
classDiagram
class BankAccount {
-balance: BigDecimal
-ownerId: UUID
-lock: Lock
+deposit(amount)
+withdraw(amount)
+getBalance() BigDecimal
}
note for BankAccount "balance is private.\nNo external code can\nset it directly."
- Fields stay
private. Mutations go through methods that enforce invariants (no negative balance, no overdraft). - Without encapsulation, every callsite duplicates business rules - and one will forget.
- The "barrier" can be a class, a module, a package, or even a process boundary.
2. Abstraction - essential, not incidental#
Expose what a thing does; hide how it does it. Abstractions are the API + the rules; implementations are the code.
classDiagram
class PaymentGateway {
<<interface>>
+charge(amount, card) ChargeResult
+refund(chargeId) RefundResult
}
class StripeGateway
class AdyenGateway
class FakeGateway
PaymentGateway <|.. StripeGateway
PaymentGateway <|.. AdyenGateway
PaymentGateway <|.. FakeGateway
- Callers depend on
PaymentGateway, not on Stripe specifics. - Swap real ↔ fake in tests; real ↔ different vendor in prod.
- Abstraction != generalisation. Adding levels "just in case" creates indirection without insight.
3. Inheritance - is-a hierarchy#
Reuse + specialise a base type's behaviour and contract.
classDiagram
class Vehicle {
+id
+seats
+start()
+stop()
}
class Car {
+trunkSize
+openTrunk()
}
class Motorcycle {
+hasSidecar: bool
}
class ElectricCar {
-batteryKwh
+chargeFor(minutes)
}
Vehicle <|-- Car
Vehicle <|-- Motorcycle
Car <|-- ElectricCar
- Subtypes inherit fields + methods, can add their own, can override existing.
- Misused for code reuse alone - prefer composition instead (see Composition over inheritance).
- Multiple inheritance: avoided in Java/C#; available via mixins/traits/interfaces in Scala, Rust, Python.
4. Polymorphism - one name, many forms#
Same operation, different behaviour depending on the runtime type.
classDiagram
class Shape {
<<abstract>>
+area() double
}
class Circle {
+r: double
+area() double
}
class Rectangle {
+w: double
+h: double
+area() double
}
class Triangle {
+base: double
+height: double
+area() double
}
Shape <|-- Circle
Shape <|-- Rectangle
Shape <|-- Triangle
Shape::area dispatches to Circle.area() / Rectangle.area() / etc. The caller doesn't care which.
Flavours#
| Flavour | Mechanism | Example |
|---|---|---|
| Subtype / dynamic dispatch | virtual methods, vtable | Shape::area above |
| Ad-hoc | method overloading | print(int) vs print(String) |
| Parametric | generics / templates | List<T>, Map<K,V> |
| Coercion | implicit conversion | int → long in arithmetic |
| Duck typing | runtime structural | Python - "if it quacks like a duck…" |
How the pillars compose#
flowchart LR
subgraph Encap[Encapsulation barriers]
M[Module / Class]
end
subgraph Abs[Abstraction]
I[Interface]
end
subgraph Inh[Inheritance hierarchy]
A[Abstract] --> B[Concrete]
end
subgraph Poly[Polymorphism]
Call[Caller] -->|same call| Many[Many impls]
end
M --> I --> A
B --> Many
classDef p fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
classDef s fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
class M,I,A,B,Call,Many s;
classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
classDef storage fill:#e5e7eb,stroke:#374151,stroke-width:1px,color:#0f172a;
classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
class M,I,A,B,Call,Many service;
- Encapsulation gives you a boundary.
- Abstraction defines what crosses that boundary.
- Inheritance shares a contract among types.
- Polymorphism lets callers work against the contract without knowing the concrete type.
Together: callers depend on abstractions; implementations are encapsulated; new behaviours plug in via inheritance + polymorphism.
Where these show up in this site#
- Notification system -
Channelis an abstraction with concretePushChannel / SMSChannel / EmailChannelsiblings (Abstraction + Polymorphism). - Logger framework -
Appenderinterface, many implementations; loggers compose appenders (Abstraction + Composition). - Parking Lot / Vending Machine / Elevator -
Vehicle,Item,Requesthierarchies use Inheritance + Polymorphism. - Payment gateway -
PaymentMethodinterface withCard / UPI / Wallet / BNPLimplementations (Abstraction).
Glossary & fundamentals#
Concepts referenced in this design. Each row links to its canonical page; the tag column shows whether it is a high-level (HLD) or low-level (LLD) concept.
| Tag | Concept | What it is | Page |
|---|---|---|---|
HLD |
Service mesh | sidecar mesh, mTLS, traffic policy | service-mesh |
LLD |
Async models | futures / async-await / coroutines / actors | async-models |
LLD |
OOP pillars | encapsulation, abstraction, inheritance, polymorphism | oop-pillars |
LLD |
Composition over inheritance | prefer composition; small swappable parts | composition-over-inheritance |
Quick reference#
Why these four (and not more)#
The "four pillars" framing is a teaching device, not a formal classification. Different communities slice OO into different sets - Alan Kay's original OO emphasises message-passing more than inheritance; modern functional-OO blends like Scala/Kotlin emphasise immutability + traits.
The four pillars survive because they're the operations a designer reaches for first when shaping a system in an OO language.
Practical rules of thumb#
- Encapsulation: if a field is
public, you've decided it's part of your forever-API. Default toprivate. - Abstraction: every dependency in your domain layer is an interface. Concretions live at the composition root.
- Inheritance: limit hierarchies to 2 levels. Any deeper, refactor to composition.
- Polymorphism: prefer it over
if (type == ...)chains. The compiler tells you when you missed a case (with sealed types / exhaustive matches).
Common myths#
- "OOP means inheritance." Inheritance is the least important pillar in modern code. Encapsulation and abstraction matter more.
- "Encapsulation = getters and setters." No - getters and setters are usually a sign of leaked encapsulation. Methods should do things, not just expose state.
- "Polymorphism is slow." On the JVM and modern C++, virtual calls are typically a single indirection; megamorphic call-site cost matters only in extreme hot loops.
Where OO falls short#
- Highly concurrent designs lean on immutability and message passing more than on encapsulating mutable state.
- Data-pipeline code reads cleaner as functions over immutable data than as objects.
- Many "OO patterns" disappear in functional languages (Strategy = a function, Visitor = pattern matching).
Use the pillars as a default; abandon any of them when a more local style serves the code better.
Refs#
- Object-Oriented Software Construction - Bertrand Meyer (1988).
- Clean Architecture - Robert C. Martin.
- Domain-Driven Design - Eric Evans (encapsulation by bounded context).
- Effective Java - items on classes & interfaces (15-25).
- Alan Kay's emails about what he meant by "object-oriented".
FAQ#
What are the four pillars of object-oriented programming?#
Encapsulation hides state behind methods, Abstraction exposes essentials, Inheritance reuses a parent's behaviour, and Polymorphism lets one name mean many forms.
What is the difference between encapsulation and abstraction?#
Encapsulation packages data with the code that operates on it. Abstraction picks which details to expose. Encapsulation enforces; abstraction designs.
Is polymorphism only about method overriding?#
No. There is also ad-hoc polymorphism (overloading) and parametric polymorphism (generics). Subtype polymorphism via overriding is the most familiar in OOP.
Should I use inheritance or composition?#
Prefer composition unless there is a clean is-a relationship and you need to substitute one type for the base. Inheritance creates tight coupling that is hard to undo.
Are OOP pillars still relevant in modern languages?#
Yes. Even functional-leaning languages reuse the ideas: encapsulation as modules, polymorphism as type classes, and so on. The vocabulary still shapes interviews.
Related Topics#
- SOLID Principles: SOLID formalizes the constraints that keep OOP pillars from becoming liabilities
- Composition over Inheritance: the practical alternative when inheritance hierarchies become brittle
- Behavioral Patterns: patterns that exploit polymorphism and encapsulation to extend behavior safely
Further reading#
Curated, high-credibility sources for going deeper on this topic.