Abstraction
You drive a car with a steering wheel and pedals, not by managing pistons. That's abstraction — and it's the OOP pillar interviewers use to test whether you can design clean interfaces that hide complexity.
The core idea
Abstraction means exposing only the essential features of something while hiding the complex implementation details. It models the what, not the how. In code you define a contract — the operations available — and let concrete classes fill in the details. Callers program to the abstraction and never depend on the messy internals.
Beginner — interfaces and abstract classes
Two main tools:
- Interface — a pure contract: a set of method signatures with no implementation (in many languages). A class that "implements" it must provide the bodies.
- Abstract class — a partial implementation: it can have some concrete methods plus abstract (unimplemented) ones, and cannot be instantiated directly.
Either way, callers depend on the abstract type, not a specific concrete class.
Intermediate — interface vs abstract class
Use an interface to define a capability many unrelated classes can share ("Comparable", "Serializable"), and when you need multiple such capabilities (a class can implement many interfaces). Use an abstract class when related classes share common code/state and you want to provide a partial base. Rule of thumb: interface = can-do capability; abstract class = is-a base with shared implementation.
Advanced — abstraction as design leverage
Abstraction underpins the Dependency Inversion Principle: high-level modules depend on abstractions, not concretions. A PaymentProcessor interface lets you swap Stripe for PayPal without changing business logic; a Repository interface lets you swap a database for an in-memory fake in tests. The risk is over-abstraction — layers of interfaces with a single implementation add indirection without benefit (YAGNI). Good abstraction is chosen at the seams where implementations genuinely vary.
Worked example
Define a Shape abstraction with an area() operation. Circle and Rectangle implement it differently, but drawing code calls shape.area() without knowing the concrete type. Add a Triangle later and the drawing code needs zero changes — it only ever depended on the abstraction. The "how" (formula per shape) is hidden; the "what" (every shape has an area) is exposed.
When to use
Introduce an abstraction where the implementation genuinely varies or may change: external services, storage, algorithms, notification channels. Depend on the abstraction so alternatives (and test doubles) plug in cleanly. Don't abstract things that will only ever have one form.
Tricks and mnemonic
Remember "Abstraction = the dashboard, not the engine." Interface = a capability contract (can-do); abstract class = a shared base (is-a).
Don't confuse abstraction with encapsulation. Encapsulation hides internal state within one class; abstraction exposes a simplified model and hides implementation across a boundary. And beware over-abstraction: an interface with exactly one implementation and no foreseeable second one is usually needless indirection.
- ✓- Abstraction exposes essential behavior (what) and hides implementation (how).
- ✓- Tools: interfaces (pure contracts) and abstract classes (partial implementations).
- ✓- Interface = can-do capability (multiple allowed); abstract class = is-a shared base.
- ✓- Enables swapping implementations and mocking in tests (dependency inversion).
- ✓- Avoid over-abstraction: don't add interfaces with a single, unchanging implementation.
- ✓Abstraction models the essential "what" and hides the "how" behind a contract, using interfaces and abstract classes. Callers depend on the abstraction, so implementations swap freely and tests mock easily. Apply it at seams where implementations vary — and resist abstracting things that never will.
Abstraction — Revision Notes
Fast revision of abstraction.
- Abstraction exposes only essential behavior (what) and hides implementation (how) behind a contract.
- Tools: interface (pure method-signature contract) and abstract class (partial implementation, can't be instantiated).
- Interface = can-do capability (a class can implement many); abstract class = is-a shared base with common code/state.
- Enables swapping implementations (Stripe↔PayPal) and mocking in tests — supports Dependency Inversion.
- Callers depend on the abstract type, not a concrete class.
- Beware over-abstraction: interfaces with one unchanging implementation add needless indirection (YAGNI).
- Encapsulation hides state within a class; abstraction hides implementation across a boundary.
Abstraction — Flashcards
Cover the answer, recall, then check. 8 cards.
Q1. What is abstraction in OOP?
A1. Exposing only essential features (the "what") while hiding implementation details (the "how").
Q2. What is an interface?
A2. A pure contract of method signatures with no implementation; implementing classes must provide the bodies.
Q3. What is an abstract class?
A3. A partially implemented class (concrete + abstract methods) that cannot be instantiated directly.
Q4. Interface vs abstract class — the rule of thumb?
A4. Interface = a can-do capability (multiple allowed); abstract class = an is-a base with shared implementation.
Q5. How does abstraction help testing?
A5. Code depends on an abstraction, so you can substitute a mock/fake implementation in tests.
Q6. Which SOLID principle does abstraction underpin?
A6. Dependency Inversion — depend on abstractions, not concretions.
Q7. What is over-abstraction?
A7. Adding interfaces/layers with a single, unchanging implementation — needless indirection.
Q8. Abstraction vs encapsulation?
A8. Abstraction hides implementation across a boundary (what is exposed); encapsulation hides internal state within a class (how it's stored).
Abstraction — Worked Example
Worked Example
Problem: A checkout flow must support Credit Card today and UPI tomorrow without rewriting checkout logic. Show how abstraction achieves this.
Solution: Abstraction exposes what an operation does while hiding how. Define an abstract type for payment and let checkout depend only on it.
interface PaymentProcessor {
boolean pay(double amount); // the "what"
}
class CreditCardProcessor implements PaymentProcessor {
public boolean pay(double amount) { /* card gateway... */ return true; }
}
class UpiProcessor implements PaymentProcessor {
public boolean pay(double amount) { /* UPI intent... */ return true; }
}
void checkout(PaymentProcessor processor, double amount) {
if (processor.pay(amount)) print("Order confirmed");
}
checkout calls processor.pay(...) without knowing the concrete class. Adding UPI means writing a new implementation and passing it in — checkout code is untouched.
Answer: Checkout depends on the PaymentProcessor abstraction, so new payment methods plug in without changing it.
- ✓- Abstraction exposes essential behaviour and hides implementation detail.
- ✓- Program to an interface, not a concrete class, to swap implementations freely.
- ✓- Abstraction is the "what"; encapsulation is the "how it's protected" — related but distinct.