To make our jobs easier
Less of this
More of this
@barryosull
@barryosull
https://barryosull.com
contact@barryosull.com
@barryosull
Separate the details from the behaviour
What you want to do
How you want to do it
Send a letter to an address
Use the postal service
Remove noise to makes things clearer
(See the forest for the trees)
@barryosull
Same behaviour, different implementations
What you want to do
How you want to do it
Send a letter to an address
Use the postal service
Deliver via Jetpack
@barryosull
Less Mess
More Clarity
<?php
namespace App\Services;
use App\ValueObjects\ {Message, Address, MessageCollection};
interface MessageDelivery
{
public function deliver(Message $message, Address $address);
public function fetch(Address $address): MessageCollection;
}
?>
<?php
namespace App\Services;
class MessageDeliveryException extends \Exception {}
?>
<?php
namespace App\Services;
class MessageDeliveryException extends \Exception {}
?>
@barryosull
<?php
namespace App\Services;
use App\Service\ {MessageDelivery, MessageDeliveryException};
use App\ValueObjects\ {Message, Address};
use PostOffice\ {Letter, PostBox, FullException};
class MessageDeliveryPostOffice implements MessageDelivery
{
private $post_box;
public function __construct(PostBox $post_box)
{
$this->post_box = $post_box;
}
public function deliver(Message $message, Address $address)
{
try {
$letter = new Letter($address->toArray(), $message->value());
$this->post_box->post($letter);
} catch (FullException $e) {
throw new MessageDeliveryException($e->getMessage(), $e->getCode(), $e);
}
}
//... fetch implementation omitted
}
?>
@barryosull
<?php namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\Env;
use App\Service\MessageDelivery;
use App\Service\ {MessageDeliveryPostBox, MessageDeliveryFake, MessageDeliveryTimer};
class AppServiceProvider extends ServiceProvider
{
public function register()
{
if ($this->app->environment() == Env::TESTING) {
$this->app->singleton(MessageDelivery::class, MessageDeliveryFake::class);
} else {
$this->app->singleton(MessageDelivery::class, function(){
return new MessageDeliveryTimer(
new MessageDeliveryPostBox()
);
});
}
}
}
@barryosull
Implementation B
Implementation A
Choosing your implementation
<?php
namespace App\Controllers\Http;
use App\Service\MessageDelivery;
use App\ValueObjects\ {Message, Address};
class MessageDeliveryController
{
private $message_delivery;
public function __construct(MessageDelivery $message_delivery)
{
$this->message_delivery = $message_delivery;
}
public function handle(Request $request)
{
$message = new Message($request->input('message'));
$address = new Address($request->input('address'));
$this->message_delivery->deliver($message, $address);
}
}
?>
@barryosull
Integration tests
<?php
namespace Tests\Integration\App\Service\MessageDelivery;
use App\Service\MessageDelivery;
abstract class MessageDeliveryTest extends \PHPUnit\Framework\TestCase
{
abstract protected function makeMessageDelivery(): MessageDelivery;
public function test_delivering_a_message()
{
$message_delivery = $this->makeMessageDelivery();
$message = new Message("Hi there");
$address = new Address(["line 1", "line 2", "Wexford", "IE"]);
$message_delivery->deliver($message, $address);
$delivered_message = $message_delivery->fetch($address);
$this->assertEquals($message, $delivered_message, "Messages should match");
}
}
@barryosull
There is more that one way to do something in the codebase
Examples:
Do not add interfaces for the sake of interfaces
@barryosull
Extracting interfaces from existing code
@barryosull
@barryosull
Captain's Log
<?php namespace Psr\Log;
interface LoggerInterface
{
public function emergency($message, array $context = array());
public function alert($message, array $context = array());
public function critical($message, array $context = array());
public function error($message, array $context = array());
public function warning($message, array $context = array());
public function notice($message, array $context = array());
public function info($message, array $context = array());
public function debug($message, array $context = array());
public function log($level, $message, array $context = array());
}
@barryosull
Let's file this away for later
<?php namespace League\Flysystem;
interface AdapterInterface extends ReadInterface
{
const VISIBILITY_PUBLIC = 'public';
const VISIBILITY_PRIVATE = 'private';
public function write($path, $contents, Config $config);
public function writeStream($path, $resource, Config $config);
public function update($path, $contents, Config $config);
public function updateStream($path, $resource, Config $config);
public function rename($path, $newpath);
public function copy($path, $newpath);
public function delete($path);
public function deleteDir($dirname);
public function createDir($dirname, Config $config);
public function setVisibility($path, $visibility);
}
@barryosull
Process:
Take an existing Laravel 5.5 App and solve problems using interfaces
@barryosull
We want to switch from our own naive implementation of a logger to the PSR3 standard
Notes:
@barryosull
Slow API (Intermediate)
A slow API is slowing down our acceptance tests. We want to use a fake one during those tests.
Notes:
@barryosull
The contacts list in admin is constantly getting hit with requests and it's impacting the DB. We want to add a cache, but we don't know which one is best.
Notes:
@barryosull
@barryosull
@barryosull
https://barryosull.com
contact@barryosull.com
https://slides.com/barryosull/workshop-using-interfaces-effectively
Workshop URL: https://github.com/barryosull/interfaces-workshop