CLOSE
Updated on 20 Jun, 202625 mins read 3 views

As we learned earlier about polymorphism.

We discovered:

Animal* animal = new Dog();
animal->makeSound();

The code works because:

Animal

defines a contact, and:

Dog

implements that contract.

This introduces one of the most important idead in software engineering:

Good sofrware depedns on contracts, not concrete implementation.

This idea is responsible for:

Modern Frameworks
Plugin Architectures
Microservices SDKs
Cloud SDKs
Database Drivers
Payment Gateways
Operating Systems
Game Engines
Enterprise Software

Without contracts, software becomes:

Rigid
Tightly Coupled
Difficult To Test
Difficult To Extend

With contracts, software becomes:

Flexible
Replaceable
Extensible
Maintainable

The Fundamental Problem

Imagine building:

Food Delivery System

You create:

class EmailService
{
public:
    void sendEmail()
    {
    }
};

Then:

class OrderService {
	private:
		EmailService: emailService;
};

Looks fine.

Later requiremnts change:

Need SMS
Need WhatsApp
Need Push Notifications
Need Slack Notifications

Now:

OrderService

is tightly coupled to:

EmailService

This is a common enterprise problem.

What Is a Contract?

A contract defines:

What must be done

without specifying:

How it is done

Example:

Payment Gateway Contract
Must Support:
	Pay()
	Refund()

Contract says:

What

Not:

How

Different implementations:

Stripe
Razorpay
PayPal
UPI

can all satisfy the same contract.

Real-World Analogy

Consider a wall socket.

The socket defines:

Voltage
Frequency
Plug Shape

The socket does NOT care whether the connected device is:

Laptop
Phone Charger
Television
Printer

The socket represents:

Interface

Device represent:

Implementations

This is contract-based design.

What Is an Interface?

Conceptual Definition

An interface defines:

A set of operations that must be provided.

In many languages:

Java
C#
TypeScript

there is a dedicated interface keyword.

C++ traditionally uses:

Pure Abstract Classes

to implement interfaces.

First Interface Example

Class IPaymentMethod
{
public:
	virtual void pay() = 0;
	virtual ~IPaymentMethod() = default;
};

Notice:

= 0

This makes the method:

Pure Virtual

Meaning:

Must be Implemented

Implementing the Interface

class CreditCardPayment :
    public IPaymentMethod
{
public:

    void pay() override
    {
        cout << "Credit Card Payment";
    }
};

Another implementation:

class UpiPayment :
    public IPaymentMethod
{
public:

    void pay() override
    {
        cout << "UPI Payment";
    }
};

Both satisfy:

IPaymentMethod

contract.

Using the Interface

void processPayment(
    IPaymentMethod& payment)
{
    payment.pay();
}

Usage:

CreditCardPayment card;
processPayment(card);

Later:

UpiPayment upi;
processPayment(upi);

No code changes required.

The Power of Abstraction

Without abstraction:

void processPayment(
    CreditCardPayment& payment)
{
}

Only supports:

CreditCardPayment

With abstraction:

void processPayment(
    IPaymentMethod& payment)
{
}

Supports:

Any Payment Method

This dramatically improves extensibility.

What Is an Abstract Class?

An abstract class is:

A class that cannot be instantiated directly.

Example:

class Animal
{
public:

    virtual void makeSound() = 0;
};

Invalid:

Animal animal;

Valid:

Dog dog;
Cat cat;

Abstract classes exist to define common behavior.

Interface vs Abstract Class

Many beginners confuse these concepts.

Interface

Contains:

Contract Only

Example:

class ILogger
{
public:

    virtual void log() = 0;

    virtual ~ILogger() = default;
};

No state.

No implementation.

Purpose:

Pure Contract

Abstract Class

Can contain:

State
Implementation
Abstract Methods

Example:

class Animal
{
protected:

    string name;

public:

    virtual void makeSound() = 0;

    void sleep()
    {
        cout << "Sleeping";
    }
};

Contains:

Data
Behavior
Contract

Comparison Table

FeatureInterfaceAbstract Class
InstantiableNoNo
StateUsually NoYes
ImplementationUsually NoYes
Contract DefinitionYesYes
Shared LogicNoYes
Primary PurposeBehavior ContractCommon Base Functionality

UML Representation

Interface:

<<interface>>
IPaymentMethod
-----------------
pay()

Implementation:

      <<interface>>
      IPaymentMethod
             ▲
             |
    --------------------
    |                  |
CreditCard        UpiPayment

Abstract Class:

<<abstract>>
Animal
-----------------
sleep()
makeSound()

Why Interfaces Matter

Imagine:

Database Layer

Bad Design:

class UserService
{
private:
	MySQLDatabases db;
};

Problem:

Switching to:

PostgreSQL
MongoDB
SQLite

requires code changes.

Better:

class IDatabase
{
public:
	virtual void save() = 0;
};

Implementations:

MySQLDatabase
PostgreDatabase
MongoDatabase

Now:

UserService

depends on:

IDatabase

instead of:

MySQLDatabase

Huge improvement.

Programming to Abstraction

One of the most famous design principles.

Bad:

MySQLDatabase database;

Better:

IDatabase& database;

Bad:

CreditCardPayment payment;

Better:

IPaymentMethod& payment;

Rule:

Depend on what something does, not what it is.

Dependency Direction

Bad architecture:

OrderService
      ↓
EmailService

OrderService depends on EmailService.

Better:

OrderService
      ↓
INotificationService
      ↑
EmailService

Now:

EmailService
SmsService
WhatsAppService

can all be substituted.

Extensibility Example

Requirement:

Add Slack Notification

Bad Design:

Modify:

OrderService

Good Design:

Create:

class SlackNotification
    : public INotificationService
{
};

Done.

No existing code changes.

This is professional design.

Testing Benefits

Interfaces dramatically improve testing.

Example:

class IEmailService
{
public:

    virtual void send() = 0;
};

Production:

EmailService

Testing:

FakeEmailService

No real emails sent.

Testing becomes easier.

This is why interfaces dominate enterprise systems.

Common Interface Naming Convention

Common C++ style:

ILogger
IDatabase
IPaymentGateway
INotificationService

Prefix:

I

indicates interface.

Not mandatory.

But common.

Interface Segregation Preview

Bad:

class IWorker
{
public:

    virtual void work() = 0;
    virtual void eat() = 0;
    virtual void sleep() = 0;
    virtual void drive() = 0;
    virtual void fly() = 0;
};

Huge interface.

Many classes need only part of it.

This violates:

Interface Segregation Principle

Common Beginner Mistakes

Mistake 1

Depending on Concrete Classes

PaymentProcessor
    ↓
StripePayment

Too rigid.

Prefer:

PaymentProcessor
    ↓
IPaymentMethod

Mistake 2

Creating interfaces for Everything

Not every class needs an interface.

Example:

Point
Rectangle
Date

Usually do not need interfaces.

Use interfaces where variation is expected.

Mistake 3

Huge Interfaces

100+ Methods

Usually poor design.

Interfaces should be focused.

Mistake 4:

Leaking Implementation Details

Bad:

class IDatabase
{
public:

    virtual MySQLConnection*
    getConnection() = 0;
};

Now interface depends on MySQL.

Defeats abstraction.

Industry Perspective

Large-scale systems rely heavily on interfaces.

Examples:

Spring Framework
ASP.NET
Qt
Unreal Engine
AWS SDK
Azure SDK
Database Drivers
Cloud Storage Systems

Why?

Because large systems must support:

Replacement
Testing
Evolution
Extension

Interfaces make this possible.

Expert Notes

One of the biggest mindset shifts:

Beginner thinks:

What class should I use?

Senior engineer thinks:

What contract should exist?

Another insight:

Interfaces are not about implementation.

They are about boundaries.

Buy Me A Coffee

Leave a comment

Your email address will not be published. Required fields are marked *

Your experience on this site will be improved by allowing cookies Cookie Policy