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
| Concept | Description |
|---|---|
| Interface | What the class does — the public API, e.g., methods and usage |
| Implementation | How it works internally — data members, method logic |
| Invariants | Rules that must always be true for the class to function correctly |
| Error Handling | Use exit() or exceptions to enforce invariants |
Linked Map of Contexts