Interfaces let you write code that depends on abstractions, not on concrete classes.
When you programming this way, your code becomes more flexible, easier to test, easier to replace, and less dependent on specific implementations.
Let’s see it in some exemples below:
//Bad :-(
//Programming to a concrete class with tightly coupled code
class MySqlDatabase {
public function connect() {
echo "Connected to MySQL";
}
}
class UserService {
private MySqlDatabase $db;
public function __construct() {
$this->db = new MySqlDatabase(); // tightly coupled
}
public function loadUser() {
$this->db->connect();
}
}
//Good :-)
//Define an interface and have decoupled code, like Lego bricks!
interface DatabaseConnection {
public function connect();
}
//Create multiple implementations
class MySqlDatabase implements DatabaseConnection {
public function connect() {
echo "Connected to MySQL";
}
}
class PostgresDatabase implements DatabaseConnection {
public function connect() {
echo "Connected to PostgreSQL";
}
}
//Use only the interface inside your main class
class UserService {
private DatabaseConnection $db;
// Any database that implements the interface can be injected
public function __construct(DatabaseConnection $db) {
$this->db = $db;
}
public function loadUser() {
$this->db->connect();
}
}
//Choose the implementation at runtime
$service = new UserService(new PostgresDatabase()); //Postgres: I Choose You!
$service->loadUser();
Sometimes it isn’t always clear how to properly segregate your interface or to predict future changes;
So, if in the future your interface becomes too bloated, don’t hesitate to divide it into smaller and more focused sections, each representing a single responsibility.
When designing interfaces, you should always strive to be as precise as possible; since they describe what the parts of your system are capable of doing, the clearer that description is, the easier it will be to build, update, and maintain your software.
Leave a Reply