Interface vs Implementation
One of the most powerful concepts in Software Engineering and OOAD is the separation of Interface and Implementation.
Definitions
- Interface: Defines what an object does (the contract). It consists of function signatures and public methods.
- Implementation: Defines how an object does it. It contains the logic, state, and private helper methods.
The Principle: Program to an Interface
When you “program to an interface,” you write code that interacts with an abstraction (an interface or abstract class) rather than a concrete class. This makes your code more flexible and easier to change.
Example: Different Storage Mechanisms
Imagine an application that needs to save data. You could save it to a File or a Database.
Wrong Way (Programming to Implementation)
class Database {
public:
void saveToDB(string data) { /* logic */ }
};
class App {
Database db; // Hard dependency on concrete class
public:
void run() { db.saveToDB("User Data"); }
};
If you want to switch to a file system, you must rewrite the App class.
Right Way (Programming to Interface)
// The Interface
class IDataStore {
public:
virtual void save(string data) = 0;
};
// Implementation 1
class DatabaseStore : public IDataStore {
public:
void save(string data) override { cout << "Saving to DB..." << endl; }
};
// Implementation 2
class FileStore : public IDataStore {
public:
void save(string data) override { cout << "Saving to File..." << endl; }
};
// The App depends on the interface
class App {
IDataStore* store;
public:
App(IDataStore* s) : store(s) {}
void run() { store->save("User Data"); }
};
Benefits of Decoupling
- Easy Swapping: You can swap implementations without changing the client code.
- Testability: You can inject “Mock” or “Fake” implementations for unit testing.
- Parallel Development: One team can work on the interface usage while another works on the implementation.
- Extensibility: You can add new implementations later without breaking existing systems.
Abstract Classes vs. Interfaces (Java/C#)
- Interfaces: Usually only contain method signatures (though modern Java allows default methods). A class can implement multiple interfaces.
- Abstract Classes: Can contain both implemented methods and abstract methods. A class can typically only inherit from one abstract class.
Java Interface Example
public interface PaymentProcessor {
void process(double amount);
}
public class StripeProcessor implements PaymentProcessor {
public void process(double amount) {
System.out.println("Processing via Stripe: $" + amount);
}
}
Summary
By separating implementation from interface, we create a layer of abstraction that protects the rest of our system from changes in low-level details. This is a recurring theme in all design patterns.