When building software, it’s essential to separate what your code does (the interface) from how it does it (the implementation). This separation allows you to design flexible, reusable, and easy-to-understand code.

🧠 Always design the interface before the implementation.


What is an Interface?

An interface defines how you interact with a piece of code — its public-facing behavior — without revealing how it works internally.

Example: Using a Circle class in a program

int main() {
    Circle c(8, -3, 3.7);
    c.scale(2);
    c.draw();
    cout << area(c) << endl;
}

This usage suggests:

  • A constructor to create a Circle
  • A scale() method to resize it
  • A draw() method to render it
  • An area() function to compute its area From this interface, we understand what the class should do, not how it does it.

What is an Implementation?

The implementation defines the internal details: how the class stores data and how the methods work.

Header/Interface + Implementation Sketch:

class Circle {
public:
    Circle(double x, double y, double r);
    void scale(double factor);
    void draw();
};
 
double area(Circle x);

This defines what is available to the user. Even without full implementations, this can compile — but linking will fail unless the definitions are provided.

Designing the Implementation

To implement a class, consider:

  • What data does each method need?
  • What assumptions or constraints should be maintained? (→ class invariants)

In our Circle class:

  • We need to store position (x, y) and radius (r)
  • The radius must always be positive (this is a class invariant)

Full Class Declaration with Invariants

class Circle {
private:
    // Class Invariant: m_r > 0
    double m_x;
    double m_y;
    double m_r;
 
public:
    Circle(double x, double y, double r);
    void scale(double factor);
    void draw();
};

Handling Errors and Invariants

Why Constructors Can’t Return Errors

C++ constructors cannot return a value, so we must handle invalid inputs in another way. One simple method: exit the program when an invariant is violated.

Circle::Circle(double x, double y, double r) {
    if (r <= 0) {
        exit(1); // Stops the program if radius is invalid
    }
    m_x = x;
    m_y = y;
    m_r = r;
}

Implementing Methods with Constraints

Let’s say we want to scale the circle’s size. The scaling factor must be strictly positive. Here’s how you can safely implement it:

bool Circle::scale(double factor) {
    if (factor <= 0) {
        return false;
    }
    m_r *= factor;
    return true;
}

✅ Good practice: Always return a value to indicate success or failure when appropriate.

🧩 Summary

ConceptDescription
InterfaceWhat the class does — the public API, e.g., methods and usage
ImplementationHow it works internally — data members, method logic
InvariantsRules that must always be true for the class to function correctly
Error HandlingUse exit() or exceptions to enforce invariants

Linked Map of Contexts