Why should we use an INTERFACE
even when we only have one concrete class?
Based on PHP
by Rafal Ksiazek, IT Leader
https://github.com/harpcio
Real example (or maybe not):
Application based on one repository
for an electronics company which has
two departments: one in Europe and one in USA
(Detroit)
Requirements:
…
Sometimes we get an extra job during the day
and we want to know how many free hours we
have
...
Interface
Without With
class Worker {
private $workedHours = 0;
public function work() {
$this->hours += 1;
}
public function getWorkedHours() {
return $this->workedHours;
}
}
class Company {
public function calculateHoursCapacity() {
$workers = $this->hrDepartment->getAllWorkers();
$freeHours = 0;
foreach ($workers as $worker) {
$freeHours += $this->hrDepartment->getFreeHoursLeft($worker);
}
return $freeHours;
}
}
class HRDepartmant {
private $hoursPerDay = 8;
public function getAllWorkers() {}
public function getFreeHoursLeft(Worker $worker) {
return $this->hoursPerDay - $worker->getWorkedHours();
}
}
class Worker implements WorkerInterface {
private $workedHours = 0;
public function work() {
$this->hours += 1;
}
public function getWorkedHours() {
return $this->workedHours;
}
}
class Company {
public function calculateHoursCapacity() {
$workers = $this->hrDepartment->getAllWorkers();
$freeHours = 0;
foreach ($workers as $worker) {
$freeHours += $this->hrDepartment->getFreeHoursLeft($worker);
}
return $freeHours;
}
}
class HRDepartmant {
private $hoursPerDay = 8;
public function getAllWorkers() {}
public function getFreeHoursLeft(WorkerInterface $worker) {
return $hoursPerDay - $worker->getWorkedHours();
}
}
Everything works perfectly
The programmer in the European department got
a $1000 bonus
The company can now handle the extra jobs.
There is one thing we can be sure of:
CHANGE
So be prepared!
New client requirement!
Our department in USA bought
new 24h workers - robots,
we need to handle that
The new functionality should be implemented by a programmer in Detroit
Interface
Without With
class Worker {
...
protected $maxHoursPerDay = 8;
…
public function getMaxHoursPerDay() {
return $this->maxHoursPerDay;
}
}
class Robot extends Worker {
protected $maxHoursPerDay = 24;
}
class HRDepartmant {
...
public function getFreeHoursLeft(Worker $worker) {
return $worker->getMaxHoursPerDay()
- $worker->getWorkedHours();
}
}
interface WorkerInterface {
...
public function getMaxHoursPerDay();
}
class Worker implements WorkerInterface {
...
private $maxHoursPerDay = 8;
…
public function getMaxHoursPerDay() {
return $this->maxHoursPerDay;
}
}
class Robot implements WorkerInterface {
private $workedHours = 0;
private $maxHoursPerDay = 24;
public function work() {
$this->hours += 1;
}
public function getWorkedHours() {
return $this->workedHours;
}
public function getMaxHoursPerDay() {
return $this->maxHoursPerDay;
}
}
class HRDepartmant {
...
public function getFreeHoursLeft(Worker $worker) {
return $worker->getMaxHoursPerDay() - $worker->getWorkedHours();
}
}
New client requirement!
Our departments in both Europe and USA
built canteens for their workers
The new functionality should be implemented by a programmer in Europe
Interface
Without With
class Worker {
...
private $meals = 0;
...
public function eat() {
$this->meals += 1;
}
...
public function getEatenMeals() {
return $this->meals;
}
}
class Company {
public function calculateMealsCost() {
$workers = $this->hrDepartment->getAllWorkers();
$mealsCost = 0;
foreach ($workers as $worker) {
$mealsCost += $this->accountantDepartment->getMealsCost($worker);
}
return $mealsCost;
}
}
class AccountingDepartment {
private $mealCost = 2; // $
public function getMealsCost(Worker $worker) {
return $worker->getEatenMeals() * $this->mealCost;
}
}
interface WorkerInterface {
public function eat();
public function getEatenMeals();
}
.. all the same code from the left side..
.. and after he started testing the application he saw something like this:
FatalErrorException in Robot.php line 21:
Error: Class ApplicationRobot contains 1 abstract method and must therefore
be declared abstract or implement the remaining methods
(ApplicationWorkerInterface::eat())
What is going on!?
The two programmers (one in Europe, one in USA) didn't have the same
knowledge about specific implementations of inherited classes.
Interface
Without With
In this case the programmer unintentionally gave the Robot class a human
behaviour in to form of the eat() method and after some manual testing he
deployed this code to production.
After a few months he was informed that the application is displaying some
strange behaviour: while the departmant in Detroit has only 150 employees,
450 meals are eaten every day.
In this case the programmer also unintentionally provided the Robot's class
with a human behaviour in the form of the eat() method, but he got a fatal error
exception.
He now has the knowledge of the Robot class and he knows that his
implementation is wrong — he can change it and make it more logical.
Interface
Without With
Pros:
● We'll build our application „faster”*
* yes, a couple of minutes less per class is really an irrefutable
fact
Cons:
● Sometimes we can give other classes
behaviours that shouldn't belong to them..
Pros:
● At some point in time, we will be able to save a
lot of our time, the client’s time, and of course
– money
● We can write tests for our classes in a better
way - i.e. FakeArrayRepositories,
FakeFileSystemManagers, FakeConnectors
(like Twitter / Facebook / Google) instead of
creating mocks everywhere..
Cons:
● We need 1-3 minutes to create an interface
Big picture of inheritance
Freelancer
Full time
worker
Worker
Intern
worker
...
Freelancer
... ...
Robots
Adding behaviour to a parent
class also unintentionally
provides child classes with this
new behaviour.
Big picture of interfaces
Freelancer
Full time
worker
Workable
interface
Intern
worker
Freelancer Robots
Eatable
interface
...
Introducing interfaces to our classes
made the application more robust.
Charge'able
interface
Inspection'able
interface
And what about the
Liskov substitution principle?
class Robot extends Worker implements WorkableInterface {
public function eat() {
throw new LogicException('Robots cannot eat');
}
}
but now in this following piece of code we need to do something like this:
foreach($workers as $worker) {
try {
$worker->eat();
} catch (Exception $e) {}
}
And this is a violation of the Liskov substitution principle..
because we cannot substitute Worker with Robot – we need to add
try/catch and we'll be duplicating these try-catches in multiple places..
… it is only a matter of time.
What about KISS
Do you remember the simpler and stupider class?
class A() {
public function doNothing() {
// yes this method really do nothing
}
}
There is another one - the simplest and stupidest
structure.. an interface!!
interface A() {
public function doNothing();
}
What about YAGNI
Yes, but YAGNI is about adding new
functionalities, and interface is a simple and
stupid API without implementation.
It's a simple contract that forces all the classes
that implement the interface to have the same
functionality... nothing more.. nothing less..
Favor composition over inheritance
Inheritance (whitebox reuse) and Composition (blackbox reuse)
If you try to reuse code by inheriting from a class
you will make the subclass dependent on the parent class.
This makes a system in many cases unnecessarily complex and less testable.
Thank you very much
for waching!

Why should we use an INTERFACE even when we only have one concrete class?

  • 1.
    Why should weuse an INTERFACE even when we only have one concrete class? Based on PHP by Rafal Ksiazek, IT Leader https://github.com/harpcio
  • 2.
    Real example (ormaybe not): Application based on one repository for an electronics company which has two departments: one in Europe and one in USA (Detroit) Requirements: … Sometimes we get an extra job during the day and we want to know how many free hours we have ...
  • 3.
    Interface Without With class Worker{ private $workedHours = 0; public function work() { $this->hours += 1; } public function getWorkedHours() { return $this->workedHours; } } class Company { public function calculateHoursCapacity() { $workers = $this->hrDepartment->getAllWorkers(); $freeHours = 0; foreach ($workers as $worker) { $freeHours += $this->hrDepartment->getFreeHoursLeft($worker); } return $freeHours; } } class HRDepartmant { private $hoursPerDay = 8; public function getAllWorkers() {} public function getFreeHoursLeft(Worker $worker) { return $this->hoursPerDay - $worker->getWorkedHours(); } } class Worker implements WorkerInterface { private $workedHours = 0; public function work() { $this->hours += 1; } public function getWorkedHours() { return $this->workedHours; } } class Company { public function calculateHoursCapacity() { $workers = $this->hrDepartment->getAllWorkers(); $freeHours = 0; foreach ($workers as $worker) { $freeHours += $this->hrDepartment->getFreeHoursLeft($worker); } return $freeHours; } } class HRDepartmant { private $hoursPerDay = 8; public function getAllWorkers() {} public function getFreeHoursLeft(WorkerInterface $worker) { return $hoursPerDay - $worker->getWorkedHours(); } }
  • 4.
    Everything works perfectly Theprogrammer in the European department got a $1000 bonus The company can now handle the extra jobs.
  • 5.
    There is onething we can be sure of: CHANGE So be prepared!
  • 6.
    New client requirement! Ourdepartment in USA bought new 24h workers - robots, we need to handle that The new functionality should be implemented by a programmer in Detroit
  • 7.
    Interface Without With class Worker{ ... protected $maxHoursPerDay = 8; … public function getMaxHoursPerDay() { return $this->maxHoursPerDay; } } class Robot extends Worker { protected $maxHoursPerDay = 24; } class HRDepartmant { ... public function getFreeHoursLeft(Worker $worker) { return $worker->getMaxHoursPerDay() - $worker->getWorkedHours(); } } interface WorkerInterface { ... public function getMaxHoursPerDay(); } class Worker implements WorkerInterface { ... private $maxHoursPerDay = 8; … public function getMaxHoursPerDay() { return $this->maxHoursPerDay; } } class Robot implements WorkerInterface { private $workedHours = 0; private $maxHoursPerDay = 24; public function work() { $this->hours += 1; } public function getWorkedHours() { return $this->workedHours; } public function getMaxHoursPerDay() { return $this->maxHoursPerDay; } } class HRDepartmant { ... public function getFreeHoursLeft(Worker $worker) { return $worker->getMaxHoursPerDay() - $worker->getWorkedHours(); } }
  • 8.
    New client requirement! Ourdepartments in both Europe and USA built canteens for their workers The new functionality should be implemented by a programmer in Europe
  • 9.
    Interface Without With class Worker{ ... private $meals = 0; ... public function eat() { $this->meals += 1; } ... public function getEatenMeals() { return $this->meals; } } class Company { public function calculateMealsCost() { $workers = $this->hrDepartment->getAllWorkers(); $mealsCost = 0; foreach ($workers as $worker) { $mealsCost += $this->accountantDepartment->getMealsCost($worker); } return $mealsCost; } } class AccountingDepartment { private $mealCost = 2; // $ public function getMealsCost(Worker $worker) { return $worker->getEatenMeals() * $this->mealCost; } } interface WorkerInterface { public function eat(); public function getEatenMeals(); } .. all the same code from the left side.. .. and after he started testing the application he saw something like this: FatalErrorException in Robot.php line 21: Error: Class ApplicationRobot contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (ApplicationWorkerInterface::eat())
  • 10.
    What is goingon!? The two programmers (one in Europe, one in USA) didn't have the same knowledge about specific implementations of inherited classes. Interface Without With In this case the programmer unintentionally gave the Robot class a human behaviour in to form of the eat() method and after some manual testing he deployed this code to production. After a few months he was informed that the application is displaying some strange behaviour: while the departmant in Detroit has only 150 employees, 450 meals are eaten every day. In this case the programmer also unintentionally provided the Robot's class with a human behaviour in the form of the eat() method, but he got a fatal error exception. He now has the knowledge of the Robot class and he knows that his implementation is wrong — he can change it and make it more logical.
  • 11.
    Interface Without With Pros: ● We'llbuild our application „faster”* * yes, a couple of minutes less per class is really an irrefutable fact Cons: ● Sometimes we can give other classes behaviours that shouldn't belong to them.. Pros: ● At some point in time, we will be able to save a lot of our time, the client’s time, and of course – money ● We can write tests for our classes in a better way - i.e. FakeArrayRepositories, FakeFileSystemManagers, FakeConnectors (like Twitter / Facebook / Google) instead of creating mocks everywhere.. Cons: ● We need 1-3 minutes to create an interface
  • 12.
    Big picture ofinheritance Freelancer Full time worker Worker Intern worker ... Freelancer ... ... Robots Adding behaviour to a parent class also unintentionally provides child classes with this new behaviour.
  • 13.
    Big picture ofinterfaces Freelancer Full time worker Workable interface Intern worker Freelancer Robots Eatable interface ... Introducing interfaces to our classes made the application more robust. Charge'able interface Inspection'able interface
  • 14.
    And what aboutthe Liskov substitution principle? class Robot extends Worker implements WorkableInterface { public function eat() { throw new LogicException('Robots cannot eat'); } } but now in this following piece of code we need to do something like this: foreach($workers as $worker) { try { $worker->eat(); } catch (Exception $e) {} } And this is a violation of the Liskov substitution principle.. because we cannot substitute Worker with Robot – we need to add try/catch and we'll be duplicating these try-catches in multiple places.. … it is only a matter of time.
  • 15.
    What about KISS Doyou remember the simpler and stupider class? class A() { public function doNothing() { // yes this method really do nothing } } There is another one - the simplest and stupidest structure.. an interface!! interface A() { public function doNothing(); }
  • 16.
    What about YAGNI Yes,but YAGNI is about adding new functionalities, and interface is a simple and stupid API without implementation. It's a simple contract that forces all the classes that implement the interface to have the same functionality... nothing more.. nothing less..
  • 17.
    Favor composition overinheritance Inheritance (whitebox reuse) and Composition (blackbox reuse) If you try to reuse code by inheriting from a class you will make the subclass dependent on the parent class. This makes a system in many cases unnecessarily complex and less testable.
  • 18.
    Thank you verymuch for waching!