Skip to content
LLD Learn/OOP Foundations & UML
Browsing as a guest. Sign in to save your progress and earn XP as you complete chapters.

Interfaces, Abstract Classes & Contracts

6 min read

You'll learn to

  • -Choose between an interface and an abstract class based on whether you need shared state/behavior or just a contract
  • -Design to interfaces so a class diagram stays extensible without the caller ever depending on a concrete implementation

Interfaces and abstract classes both let you define a contract that multiple implementations can satisfy, and interview candidates frequently blur the two together. The distinction that actually matters: an interface defines a pure contract with no shared implementation, while an abstract class can define a contract and also carry shared state or partial implementation that subclasses inherit for free.

Choosing Between Them

  • -Use an interface when unrelated classes need to satisfy the same contract but share no common implementation - e.g. Comparable, Serializable, or a PaymentStrategy that CreditCard, PayPal, and Wallet all implement differently.
  • -Use an abstract class when subclasses share real state or behavior in addition to the contract - e.g. an abstract Shape class that stores a color field and provides a concrete describe() method, while leaving area() abstract.
  • -Most languages allow implementing multiple interfaces but only extending one class - so interfaces compose more freely, which matters when a class needs to satisfy several unrelated contracts at once.

Designing to the Interface, Not the Implementation

The habit that actually pays off in an interview: have calling code depend on the interface type, never the concrete class. A NotificationService should hold a reference typed as MessageSender, not as EmailSender specifically - even if, today, EmailSender is the only implementation. This single habit is what makes Dependency Inversion (a later chapter) and most creational patterns possible, because new implementations can be swapped in without touching the caller at all.

Depending on the contract, not the concrete class
from abc import ABC, abstractmethod

class MessageSender(ABC):
    @abstractmethod
    def send(self, message: str) -> None: ...

class EmailSender(MessageSender):
    def send(self, message: str) -> None:
        print(f"Emailing: {message}")

class NotificationService:
    def __init__(self, sender: MessageSender):  # depends on the interface
        self._sender = sender

    def notify(self, message: str) -> None:
        self._sender.send(message)

A quick tell for interview whiteboards: if two classes need the exact same contract but truly nothing else in common, reach for an interface. If they would end up duplicating the same field or the same method body, that shared piece belongs in an abstract class.

Interview Signal

You're designing a PaymentStrategy contract used by CreditCard, PayPal, and Wallet, which share no common state or logic. Interface or abstract class?

Weak Answer

"Abstract class, since that's what I'm more used to writing."

Strong Answer

"Interface - these three implementations don't share any state or behavior, they just need to satisfy the same pay() contract differently. An abstract class would either force empty shared state or trick me into adding coupling that isn't actually there. If they later shared real logic, I'd revisit that."

Check Yourself1 / 3

What is the core difference between an interface and an abstract class?

ScaleDojo Logo
Initializing ScaleDojo