Interfaces and Implementations
You can think of an interface as a contract, wich defines an application need. If the application nee can be or must be fulfilled by mutliple implementations, than an interface can be used.
In other words, we use interfaces when we plan on having or needing multiple implementations of an interface.
For example, if our application sends notifications, we might define a notification interface. Then we can implement an SES notifier to use Amazone SES, a Mandrill notifier to use Mandrill and other implementations for other mail systems.
The interface ensures that particular methods are available for our application to use, no matter wat implementation is decided upon.
For example, the notifier interface might look like this:
interface Notifier {
public function notify(Message $message);
}
We know any implementation of this interface must have the notify method. This let's us define the interface as a dependency in other place of out application.
The application doesn't care which implementation it uses. It just cares that the notify method exists for it to use.
class SomeClass {
public function __construct(Notifier $notifier);
{
$this->notifier = $notifier;
}
public function doStuff()
{
$to = 'some@email.com';
$body = 'This is a message';
$message = new Message($to, $body);
$this->notifier->notify($message);
}
See how SomeClass doesn't specify a specific implementation, but rather simply requires a subclass of Notifier. This means we can use our SES, Mandrill or any other implementation.
This highlights an important way interfaces can add maintainability to our application. Interfaces make changing our application notifier easier - we can simply add a new implementation and be done with it.
class SesNotifier implements Notifier {
public function __construct(SesClient $client)
{
$this->client = $client;
}
public function notify(Message $message)
{
$this->client->send([
'to' => $message->to,
'body' => $message->body
]);
}
}
In the above Example, we've used an implementation making use of Amazon's Simple Email Service (SES). However, what if we need to switch to Mandrill to send emails, or even switch to Twillio, to send SMS? As we've seen, we can easily make additional implementations and switch between those implementations as needed.
// Using SES Notifier
$sesNotifier = new SesNotifier(...);
$someClass = new SomeClass($sesNotifier);
// Or we can use Mandrill
$mandrillNotifier = new MandrillNotifier(...);
$someClass = new SomeClass($mandrillNotifier);
// This will work no matter which implementation we use
$someClass->doStuff();
Our frameworks make liberal use of interfaces in a similar fashion. In fact, frameworks are useful because they handle many possible implementations we developers may need - for example, different SQL servers, email systems, cache drivers and other services.
Frameworks use interfaces because they increases the maintainability of the framework - it becomes easier to add or modify features, and easier for us developers to extend the frameworks should we need to.
The use of an interface helps us properly encapsulate change here. We can simply make new implementations as needed!