Interface As A Contract
What is great with contracts agreements? It defines all the ways how two objects can interact.
So, your interface must define all the things what the implementation must do and may return or throw. And this definition should be as strong as possible.
The more strict you are at definitions the less tests you need to write: most of the work will be done for you by compiler. PHP guys, sorry, it’s not true for you.
<?php // RedisQueueInterface
/**
* Interface for all redis based queues
* @package Queue
*/
interface RedisQueueInterface {
/**
* Pushes $object to a queue
* The implementation MUST serialize given object before pushing.
*
* @param mixed $object
* @return void
* @throws QueueException in case of something went wrong during the push process
*/
public function push($object);
/**
* Pops $object from a queue
* The implementation MUST deserialize given object after popping
* The implementation MUST return null if queue is empty
*
* @return mixed|null
* @throws QueueException in case of something went wrong during the extraction
*/
public function extract();
/**
* @return int
*/
public function length();
}
I use mixed
type just because PHP is not strong-typed language and we have no generics. In Scala I’ll prefer to create type-parametrized trait like Queue[T]
.
Explain what, not how
Another great thing in contract is that it describes only what should be done, not how. So, your interface should declare what the implementation must do and hide all possible details.
That’s why RedisQueueInterface is bad name: it reveals implementation detail - vendor name.
<?php // QueueInterface.php
interface QueueInterface {
// Exactly the same definitions here
}
Interface Segregation
The last, but not the least great thing about contracts is that it defines only the things contractors must or must not do to achieve the goal of contract. And nothing else.
That’s why QueueInterface is bad name for interface. It defines functions for both pushing and polling the queue. It should be divided into several interfaces:
<?php
interface QueuePusher {
public function push($object);
}
interface QueueExtractor {
public function extract();
public function length();
}
interface QueueInterface extends QueuePusher, QueueExtractor {
}
Ok, sometimes I will need the whole bunch of functions of Queue, so I decided to leave QueueInterface
and extend it from QueuePusher
and QueueExtractor
.