DEV Community

Saras Growth Space
Saras Growth Space

Posted on

LLD Object-Oriented Design: Interfaces & Abstract Classes (Designing Contracts)

After understanding composition, inheritance, and polymorphism, the next important step is learning how to define clear behavioral contracts between components.

This is where interfaces and abstract classes become essential.

They help answer one of the most important questions in system design:

“What should this component be capable of doing?”

without tightly coupling the system to a specific implementation.


Why Contracts Matter in System Design

In real systems:

  • multiple implementations exist
  • components evolve independently
  • implementations may change over time

If systems depend directly on concrete classes:

  • flexibility decreases
  • testing becomes difficult
  • coupling increases

Contracts solve this problem.


What is an Interface?

An interface defines:

A capability contract that implementing classes must follow.

It specifies:

  • what operations are available
  • not how they are implemented

Example

class Payment:
    def pay(self):
        pass
Enter fullscreen mode Exit fullscreen mode

Any implementation must provide:

  • pay()

But implementation details remain independent.


Core Idea

Interfaces define capability expectations, not implementation details.


What is an Abstract Class?

An abstract class provides:

Partial implementation along with abstract behavior contracts.

It is useful when:

  • some logic is common across implementations
  • some behavior varies by subclass

Example

class Notification:
    def log(self):
        print("Logging notification")

    def send(self):
        pass
Enter fullscreen mode Exit fullscreen mode

Here:

  • log() → shared behavior
  • send() → implementation-specific behavior

Interface vs Abstract Class

Interface Abstract Class
defines contract defines partial behavior
focuses on capability focuses on shared structure
minimal implementation can contain implementation
enables loose coupling enables reuse + extension

Key Mental Model

Think of it this way:

Interface

“What can this object do?”

Abstract Class

“What common behavior already exists?”


Real System Example

Payment System

Contract

class Payment:
    def pay(self):
        pass
Enter fullscreen mode Exit fullscreen mode

Implementations

class CardPayment(Payment):
    def pay(self):
        print("Processing card payment")

class UpiPayment(Payment):
    def pay(self):
        print("Processing UPI payment")
Enter fullscreen mode Exit fullscreen mode

Usage

def process(payment: Payment):
    payment.pay()
Enter fullscreen mode Exit fullscreen mode

Now the system depends on:

  • abstraction not:
  • concrete implementation

Why Interfaces Matter in LLD

Interfaces help achieve:


1. Loose Coupling

Components communicate through contracts instead of implementations.


2. Extensibility

New implementations can be added safely.


3. Testability

Mock implementations become easy.

Example:

  • MockPayment
  • FakeNotificationService

4. System Scalability

Independent teams can build implementations separately while following the same contract.


Why Abstract Classes Matter

Abstract classes help when:

  • logic is partially shared
  • duplication needs reduction
  • related classes have common behavior

They provide:

  • controlled reuse
  • shared structure

Common Mistake

Beginners often:

  • create interfaces for everything
  • or avoid abstractions entirely

Both create problems.


Over-Abstraction

Too many unnecessary interfaces lead to:

  • complexity
  • indirection
  • unreadable systems

Under-Abstraction

No contracts lead to:

  • tight coupling
  • rigid systems
  • difficult testing

Design Principle

Depend on abstractions for flexibility, but introduce abstractions only when variation or decoupling is actually needed.


Real-World Analogy

Think of a charging socket.

The socket defines:

  • voltage standard
  • connection contract

Different chargers:

  • Apple
  • Samsung
  • Laptop adapters

all work because they follow the same contract.

The socket does not care about internal implementation.


One-Line Takeaway

Interfaces define what a component must be capable of doing, while abstract classes help share common behavior across related implementations.

Top comments (0)