Practical Object-Oriented Design Principles · The dependency inversion principle states that...

312
Practical Object-Oriented Design Principles Brandon Savage

Transcript of Practical Object-Oriented Design Principles · The dependency inversion principle states that...

Page 1: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Practical Object-Oriented Design Principles

Brandon Savage

Page 2: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

What is object-oriented design?

Page 3: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Focus on objects

Page 4: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Focus on extensibility

Page 5: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Focus on reusability

Page 6: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Focus on ease of maintenance

Page 7: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Basic Principles

Page 8: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

SOLID

Page 9: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

• Single Responsibility Principle

• Open/Closed Principle

• Liskov Substitution Principle

• Interface Segregation Principle

• Dependency Inversion Principle

Page 10: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

SOLID is a great acronym. But let’s mix up

the order.

Page 11: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Dependency Inversion

Page 12: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

(This is not the dependency injection

principle)

Page 13: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

The dependency inversion principle states that high-level modules should not depend on

low-level modules. Both should depend on abstractions.

Also, abstractions should not depend on details; details should depend on abstractions.

Page 14: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

What is Dependency Injection?

Page 15: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Hard-Coded Dependencies

<?php

class Controller { public function __construct() { $this->cache = new ApcCache(); } public function manageData() { return $this->cache->get('someKey'); } }

Page 16: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Hard-Coded Dependencies

<?php

class Controller { public function __construct() { $this->cache = new ApcCache(); } public function manageData() { return $this->cache->get('someKey'); } }

Page 17: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Easy to use.

Hard to test.

Page 18: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Easy to use

<?php

$controller = new Controller();

Page 19: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Easy to use.

Impossible to substitute.

Page 20: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

No substitution.<?php

class Controller { public function __construct() { $this->cache = new ApcCache(); } public function manageData() { return $this->cache->get('someKey'); } }

Page 21: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Service location

Page 22: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Service Location<?php

class Controller { public function __construct($cache = 'APCCache') { $this->cache = CacheLocator::locate($cache); } public function manageData() { return $this->cache->get('someKey'); } }

Page 23: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Service Location<?php

class Controller { public function __construct($cache = 'APCCache') { $this->cache = CacheLocator::locate($cache); } public function manageData() { return $this->cache->get('someKey'); } }

Page 24: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Service Location<?php

class Controller { public function __construct($cache = 'APCCache') { $this->cache = CacheLocator::locate($cache); } public function manageData() { return $this->cache->get('someKey'); } }

Page 25: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Now we’re tied to the locator.

Page 26: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Dependency Injection

Page 27: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Dependency Injection<?php

class Controller { public function __construct(APCCache $cache) { $this->cache = $cache; } public function manageData() { return $this->cache->get('someKey'); } }

Page 28: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Dependency Injection<?php

class Controller { public function __construct(APCCache $cache) { $this->cache = $cache; } public function manageData() { return $this->cache->get('someKey'); } }

Page 29: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Harder to use.

Easier to test.

Page 30: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Harder to use

<?php

$controller = new Controller(new APCCache);

Page 31: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Harder to use.

Easier to substitute.

Page 32: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

That’s dependency injection.

No more, no less.

Page 33: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Dependency Inversion

Page 34: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Dependency Injection refers to the strategy for providing objects to other objects.

It describes a methodology for how to manage dependent objects.

Page 35: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

The dependency inversion principle states that high-level modules should not depend on

low-level modules. Both should depend on abstractions.

Also, abstractions should not depend on details; details should depend on abstractions.

Page 36: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

A Caching Interface<?php

interface Cache {

public function get($key);

public function set($key, $value);

public function delete($key);

// Purge the whole cache public function purge();

}

Page 37: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Dependency Injection<?php

class Controller { public function __construct(APCCache $cache) { $this->cache = $cache; } public function manageData() { return $this->cache->get('someKey'); } }

Page 38: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Dependency Inversion<?php

class Controller { public function __construct(Cache $cache) { $this->cache = $cache; } public function manageData() { return $this->cache->get('someKey'); } }

Page 39: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

We rely on the abstraction of the Cache

interface.

Page 40: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

APCCache can’t easily be substituted (LSP Violation)

Page 41: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

By relying on the interface, we make

testing easier.

Page 42: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

A Development Cache<?php

class VoidCache implements Cache {

public function get($key) { return null; }

public function set($key, $value) { return true; }

public function delete($key) { return true; }

// Purge the whole cache public function purge() { return true; } }

Page 43: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Creating Objects

<?php

// Production $controller = new Controller(new APCCache);

// Development $controller = new Controller(new VoidCache);

Page 44: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

How can we do proper dependency injection in

PHP?

Page 45: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

How can we switch dependencies between

production and development?

Page 46: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

DI Containers

Page 47: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

A DI container is simply a programatic way of

injecting dependencies.

Page 48: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

A DI Container<?php

class Container { /** * @var array */ protected $registry = [];

/** * @param $className * @param callable $builder */ public function register($className, callable $builder) { $this->registry[$className] = $builder; }

/** * @param $className * @return mixed */ public function build($className) { if (!isset($this->registry[$className])) { throw new \InvalidArgumentException(

$className . ' is not registered'); }

return $this->registry[$className](); } }

Page 49: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Caching Strategies<?php

// production.php

return ['cacheStrategy' => 'APCCache',

];

// dev.php

return ['cacheStrategy' => 'VoidCache',

];

Page 50: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Building Our Objects

<?php

$di->register('Controller', function() use ($config, $di) { return new Controller( $di->build($config['cacheStrategy']) ); });

Page 51: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

PHP DI Containers

• Pimple

• PHP-DI

• Aura.Di

• All Major Frameworks

Page 52: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

DI containers belong in the bootstrapping layer.

Page 53: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

DI Container Antipattern<?php

class User_Controller { public function __construct(Di $di) { $this->di = $di; }

public function getUserContacts() { $cache = $di->get('cache'); if ($contacts = $cache->get('user_contacts')) { return $contacts; } } }

Page 54: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Service location is not dependency inversion!

Page 55: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Not Dependency Inversion<?php

function delete($id) { $profile = Profile::prospect()->find($id);

if (!$profile) { return Rest::notFound(); } if ($profile->delete()) { return Rest::okay([]); } return Rest::conflict(); }

Page 56: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

If we’re using abstractions, we can easily substitute one object for another.

Page 57: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Liskov Substitution Principle

Page 58: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

The Liskov substitution principle states that objects in a program should be replaceable

with instances of their subtypes without altering the correctness of the program.

E.g. if A is a type of D, then A can replace instances of D.

Page 59: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

We’re programming to an interface, not an implementation.

Page 60: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Design by contract

Page 61: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Design by contract is a philosophy which states that designers should define formal,

precise and verifiable interface specifications for software components.

Page 62: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

A Contract<?php

interface Cache {

public function get($key);

public function set($key, $value);

public function delete($key);

// Purge the whole cache public function purge();

}

Page 63: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

All instances of Cache can be substituted for

one another!

Page 64: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Substitution

<?php

class Controller { public function __construct(Cache $cache) { $this->cache = $cache; } }

$controller = new Controller(new File());

$controller2 = new Controller(new APC());

Page 65: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

What happens if our subclass changes the

interface?

Page 66: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Caching Interface<?php

interface Cache {

public function get($key);

public function set($key, $value);

public function delete($key);

// Purge the whole cache public function purge();

}

Page 67: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Extending the Interface

<?php

class MemcacheCache implements Cache { // all the other methods public function reconnect() { $this->memcache->connect($this->host, $this->port); } }

Page 68: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

LSP Breaks<?php

class User_Controller { public function __construct(Cache $cache) { $this->cache = $cache; }

public function getUserContacts() { $cache->reconnect(); if ($contacts = $cache->get('user_contacts')) { return $contacts; } } }

Page 69: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Open/Closed Principle

Page 70: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Software entities (classes, modules, functions, etc.) should be open for extension, but closed

for modification.

Page 71: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Two kinds of open/closed principle

Page 72: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Meyer’s Open/Closed Principle

Page 73: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Classes are open for extension through inheritance, but closed

to internal modification.

Page 74: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

- Bertrand Meyer

A class is closed, since it may be compiled, stored in a library, baselined, and used by client classes. But it is also open, since any new class

may use it as parent, adding new features. When a descendant class is defined, there is no need to change the original or to disturb its

clients.

Page 75: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Polymorphic Open/Closed Principle

Page 76: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Once defined, the interface cannot be changed (but

the internals can)

Page 77: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

“Interface” refers to the publicly visible methods.

Page 78: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

While the original interface should be honored, implementation is up

to the developer.

Page 79: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

The interface should not be modified.

Page 80: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

When modifying the interface, you change an

object’s type.

Page 81: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Class vs. Type

Page 82: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Class is the name of the class that defines a particular object.

Page 83: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Type is the behavior of the object as expressed through public methods.

Page 84: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Extending the interface changes the object’s

type.

Page 85: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

New Object Type<?php

class User_Controller { public function __construct(MemcacheCache $cache) { $this->cache = $cache; }

public function getUserContacts() { $cache->reconnect(); if ($contacts = $cache->get('user_contacts')) { return $contacts; } } }

Page 86: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

The return type is part of the interface, too!

Page 87: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Single Return Type

Page 88: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

(All Others Use Exceptions)

Page 89: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

PHP 7 Return Type Declaration

Page 90: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Return Type Declaration<?php

interface Cache {

public function get($key): string;

public function set($key, $value): bool;

public function delete($key): bool;

// Purge the whole cache public function purge(): bool;

}

Page 91: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

So if extending the interface isn’t allowed, what kind of interfaces do we want?

Page 92: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Interface Segregation

Page 93: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

The interface segregation principle states that no object should be forced to depend upon or

implement methods it does not use.

Smaller interfaces are better.

Page 94: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Small Interfaces

<?php

interface Countable { public function count(); }

Page 95: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Small Interfaces<?php

interface Iterator extends Traversable { public function current(); public function key(); public function next(); public function rewind(); public function valid(); }

Page 96: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Small Interfaces<?php

class ArrayClass implements Iterator, Countable { public $collection = []; public function count() { return count($this->collection); }

public function current() { return current($this->collection); }

public function key() { return key($this->collection); }

public function next() { return next($this->collection); }

public function rewind() { reset($this->collection); }

public function valid() { return (bool) $this->current(); } }

Page 97: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Objects can have more than one type!

Page 98: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Object Types<?php

class ArrayClass implements Iterator, Countable { public $collection = []; public function count() { return count($this->collection); }

public function current() { return current($this->collection); }

public function key() { return key($this->collection); }

public function next() { return next($this->collection); }

public function rewind() { reset($this->collection); }

public function valid() { return (bool) $this->current(); } }

Page 99: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Small interfaces help us keep objects discrete and single-focused.

Page 100: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Single Responsibility Principle

Page 101: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

The single responsibility principle states that every class should have a single responsibility

and that responsibility should be entirely encapsulated by that class. All its services

should be narrowly aligned with that responsibility.

Page 102: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

One class, one job.

Page 103: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

One class, one job.

Page 104: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

One class, one responsibility.

Page 105: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

One domain.

Page 106: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

A class can have many jobs, but a single domain.

Page 107: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

PDO’s Jobs

• Begin and end transactions

• Establish and maintain connection to the database.

• Send simple queries to the database.

• Prepare complex queries, returning a statement object.

Page 108: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

PDOStatement’s Jobs

• Represent a single PDO statement.

• Execute the PDO statement.

• Return the results.

Page 109: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

PDO’s domain is over the connection and queries

to the connection.

Page 110: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

PDOStatement’s domain is over a specific prepared statement and its results.

Page 111: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

…entirely encapsulated…

Page 112: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

“A single reason to change.” ~ Robert Martin

Page 113: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

- Robert Martin

Gather together the things that change for the same reasons. Separate those things that

change for different reasons.

Page 114: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Law of Demeter

Page 115: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Who can my objects talk to?

Page 116: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

What should my objects know?

Page 117: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

The Law of Demeter (LoD) states that objects should have limited knowledge. Specifically,

they should have limited knowledge about other units, only talk to its friends, not strangers, and

only talk to its immediate friends.

Page 118: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Limited knowledge

Page 119: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Unit Knowledge

<?php

class MyController { public function __construct( Memcache $memcacheConnection ) { $this->cache = new Memcache_Cache($memcacheConnection); } }

Page 120: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Only Close Friends

<?php

class MyController { public function someMethod() { $this->object ->someParameter ->someOtherParameter ->someMethod(); } }

Page 121: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Talk only to close friends.

Page 122: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Formal Law of Demeter

• The Object itself.

• The method’s parameters.

• Any objects created/instantiated by the method.

• The Object’s component objects.

From Wikipedia

Page 123: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Advantages

Page 124: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Lower coupling.

Page 125: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Maintainability.

Page 126: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Adaptability.

Page 127: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Disadvantages

Page 128: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Lots of wrapper calls.

Page 129: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

May not be entirely practical.

Page 130: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Antipatterns In Object-Oriented Design

Page 131: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Singleton

Page 132: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

The singleton pattern is a software design pattern that restricts the instantiation of a class to one object. This is useful when exactly one object is needed to coordinate actions across

the system.

Page 133: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

The Singleton<?php

class MySingleton {

private static $instance;

private function __construct() {}

private function __clone() {}

public static function getInstance() { if (!(self::$instance instanceof MySingleton)) { self::$instance = new MySingleton(); } return self::$instance; }

}

Page 134: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Singletons themselves aren’t bad.

Using them inappropriately is bad.

Page 135: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Most problems can be solved without a

singleton.

Page 136: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Tight Coupling

Page 137: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.
Page 138: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Tight Coupling slowly strangles a code base.

Page 139: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Law of Demeter

Page 140: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Untestable Code

Page 141: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Tightly coupled code can’t be easily tested.

Page 142: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Untestable

<?php

class Database { public function __construct() { $this->cache = new ApcCache(); } }

Page 143: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Testable

<?php

class Database { public function __construct(Cache $cache) { $this->cache = $cache; } }

Page 144: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Proper testing often requires stubs, substitutes,

mocks and doubles.

Page 145: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Premature Optimization

Page 146: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

It can be tempting to optimize your code as

you write it.

Page 147: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Avoid the temptation.

Page 148: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

When optimizing, you need real-world

conditions.

Page 149: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

When optimizing, you need to measure.

Page 150: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Avoid micro-optimizations.

Page 151: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Avoid being clever.

Page 152: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Don’t Repeat Yourself

Page 153: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Duplication is a hallmark of tight coupling

Page 154: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Duplicated code means duplicated tests.

Page 155: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

N duplication means N opportunities for

mistakes.

Page 156: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Indescriptive Naming

Page 157: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Code is written for humans.

Communicate purpose.

Page 158: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Pick names that matter.

Page 159: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Bad names

$i

$abc$def

$foo

$thing$that

Page 160: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Good names

$forIterator

$cache$masterDatabase

$deleteUserCommand

$jobQueue$createUserEvent

Page 161: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Use actual words in docblocks!

Page 162: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Bad Dockblock

<?php

/** * @param $xdd */ public function processXdd($xdd) { // do something }

Page 163: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Good Docblock

<?php

/** * Processes a single XDD for removal and archive. * * @param $xdd */ public function processXdd($xdd) { // do something }

Page 164: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Exceptions

Page 165: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Exceptions are the default error handling for object-oriented programming.

Page 166: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Exceptions are objects.

Page 167: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Exceptions are objects<?php

$object = new Exception;

var_dump($object);

// object(Exception)[1] protected 'message' => string '' (length=0) private 'string' => string '' (length=0) protected 'code' => int 0 protected 'file' => string '/Users/brandon/Sites/test.php' (length=29) protected 'line' => int 3 private 'trace' => array (size=0) empty private 'previous' => null

Page 168: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Exceptions are exceptional!

(not for control flow)

Page 169: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

You shouldn’t expect an exception.

(but you should handle it)

Page 170: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

The Exception API

Page 171: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Exceptions are Extensible

<?php

class MyException extends Exception {}

Page 172: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Exceptions are extended to make it easier to differentiate between different kinds of errors.

We can catch specific types of exceptions through type hinting.

Page 173: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Throwing An Exception

<?php

throw new MyException('Something went wrong');

Page 174: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Exceptions are thrown in PHP.

Page 175: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Exceptions are special - they’re the only objects

you can throw.

Page 176: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Handling an exception means you caught it.

Page 177: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Catching Exceptions

<?php

function caughtExceptions() { try { // do something dangerous } catch (Exception1 $e) { // Do stuff } catch (Exception2 $e) { // do other stuff. } }

Page 178: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Catching Exceptions

<?php

function caughtExceptions() { try { // do something dangerous } catch (Exception1 $e) { // Do stuff } catch (Exception2 $e) { // do other stuff. } }

Page 179: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

We type-hint the exception.

<?php

function caughtExceptions() { try { // do something dangerous } catch (Exception1 $e) { // Do stuff } catch (Exception2 $e) { // do other stuff. } }

Page 180: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

We can catch multiple exception types.

<?php

function caughtExceptions() { try { // do something dangerous } catch (Exception1 $e) { // Do stuff } catch (Exception2 $e) { // do other stuff. } }

Page 181: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Multiple exception catching rules

• PHP will execute the first block that matches the exception type.

• You can rethrow an exception, but it will only be caught if it’s inside a try-catch block.

• Type-hint on specific exceptions first, before type-hinting on broader exception types (like \Exception)

Page 182: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Exception Catching Rules

<?php

class MyException extends Exception {}

class MyExceptionTwo extends MyException {}

Page 183: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Exception Catching Rules

<?php

try { throw new MyExceptionTwo('test'); } catch (MyException $e) { echo 'MyException'; } catch (MyExceptionTwo $e) { echo 'MyExceptionTwo'; } catch (Exception $e) { echo 'Exception'; }

Page 184: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Exception Catching Rules

<?php

try { throw new MyExceptionTwo('test'); } catch (MyException $e) { echo 'MyException'; } catch (MyExceptionTwo $e) { echo 'MyExceptionTwo'; } catch (Exception $e) { echo 'Exception'; }

Page 185: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Exception Catching Rules

<?php

try { throw new MyExceptionTwo('test'); } catch (MyExceptionTwo $e) { echo 'MyExceptionTwo'; } catch (MyException $e) { echo 'MyException'; } catch (Exception $e) { echo 'Exception'; }

Page 186: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Exception Catching Rules

<?php

try { throw new MyExceptionTwo('test'); } catch (MyExceptionTwo $e) { echo 'MyExceptionTwo'; } catch (MyException $e) { echo 'MyException'; } catch (Exception $e) { echo 'Exception'; }

Page 187: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Finally

Page 188: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Finally<?php

try { throw new MyExceptionTwo('test'); } catch (MyExceptionTwo $e) { echo 'MyExceptionTwo'; } catch (MyException $e) { echo 'MyException'; } catch (Exception $e) { echo 'Exception'; } finally { echo "This will always be executed."; }

Page 189: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Finally is always executed.

Even if the exception is unhandled.

Page 190: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

In PHP 7, errors become exceptions.

Page 191: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Errors as ExceptionsThrowable

Error Exception

AssertionError ParseError TypeError

Page 192: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

All exceptions now implement the Throwable

interface.

Page 193: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Throwable Interface

<?php

interface Throwable { public function getMessage(); public function getCode(); public function getFile(); public function getLine(); public function getTrace(); public function getTraceAsString(); public function getPrevious(); public function __toString();

}

Page 194: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

All errors are catchable.

Page 195: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

(You should not ignore fatal errors)

Page 196: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Hint on “Throwable” to catch all errors.

Page 197: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

You can’t implement Throwable yourself.

Page 198: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

No Implementing Throwable

<?php

class ExceptionA implements Throwable { }

new ExceptionA;

//

Fatal error: Class ExceptionA cannot implement interface Throwable, extend Exception or Error instead

Page 199: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

PHP 7.1: Multi-Catch Exception Handling

Page 200: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Exception Catching Rules

<?php

try { throw new MyExceptionTwo('test'); } catch (MyExceptionTwo | MyException $e) { echo 'MyExceptionTwo'; } catch (Exception $e) { echo 'Exception'; }

Page 201: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Rules for Exception Handling

Page 202: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

#1. Always handle exceptions at the lowest logical layer, rather than letting them bubble up.

E.g. if a database layer throws an exception, handle it in the layer that called the database

layer.

Page 203: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

#2. Each layer should be capable of throwing its own exceptions, and should not rely on the

exceptions of another layer.

E.g. if your model receives a database exception it cannot handle, it should throw its own exception. It’s okay to wrap the database

exception.

Page 204: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

#3. If something can go wrong, it should have an expected exception.

Page 205: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

#4. Do not allow for a return value of null from a method on an error condition. Throw an

exception instead.

Page 206: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Bad

<?php

function checkMath($a, $b, $answer) { if($a + $b == $answer) { return $answer; } else { return false; } }

Page 207: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Good

<?php

function checkMath($a, $b, $answer) { if($a + $b == $answer) { return $answer; } else { throw new \Exception('The math didn\'t check out!'); } }

Page 208: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

#5. Expect exceptions.

Page 209: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Action Domain Responder

Page 210: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

How we do it now: Model-View-Controller

Page 211: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Classic MVCModel

View Controller

User

Page 212: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

MVC has a number of problems.

Page 213: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

We don’t actually follow this.Model

View Controller

User

Page 214: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

We follow this.Model

View Controller

User

Page 215: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Actually, we kinda do this.Model

Template Controller

User

Page 216: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Our controller ends up doing all the work.

Page 217: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Model Controller View

Request

Page 218: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Model Controller View

Request

Page 219: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Model Controller View

Request

Business Logic

Page 220: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Model Controller View

RequestAlgorithms

Database Logic

Page 221: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Model Controller View

RequestAlgorithms

Database Logic

Template System

Page 222: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Model Controller View

RequestAlgorithms

Database Logic

Template System

Page 223: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Model Controller View

RequestAlgorithms

Database Logic

Template System

Authentication

Page 224: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Model Controller View

RequestAlgorithms

Database Logic

Template SystemAuthentication

Page 225: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

That’s a lot of responsibilities.

Page 226: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Introducing a better way

Action-Domain-Responder

Page 227: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Action-Domain-Responder

Action

ResponderDomain

Page 228: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

ADR solves many problems associated with

MVC.

Page 229: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Action vs. Controller• Controllers contain several related methods.

Actions are specific to a particular request.

• Controllers interact with both the model and the presentation layer. Actions interact only with the model.

• Actions are responsible for shuffling request data into the domain, and response data out.

Page 230: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Model vs. Domain

• Models are (classically) expected to interact directly with the view. Domains do not interact with the responder.

• The domain does not modify the view directly.

Page 231: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

View vs. Responder• “The template is not the view.”

• Responders have a 1:1 relationship to specific actions.

• Responders are responsible for determining how to respond to the request.

• Responders may incorporate a template system.

• The action does not know anything about the presentation logic. This is entirely the responder’s domain.

Page 232: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Action Domain Responder

Page 233: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Action Domain Responder

Request

Page 234: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Action Domain Responder

Request

Page 235: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Action Domain Responder

Request

Business Logic

Page 236: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Action Domain Responder

Request Business Logic

Page 237: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Action Domain Responder

Request Business Logic

Template System

Page 238: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Action Domain Responder

Request Business Logic Template System

Page 239: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Action Domain Responder

Request Business Logic Template System

Authentication

Page 240: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Action Domain Responder

Request Business Logic Template SystemAuthentication ACLs

Page 241: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

PSR-7 And HTTP Messages

Page 242: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

The problem we’re trying to solve

Page 243: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

We want a common way of accepting requests and dispatching responses via HTTP.

PHP was developed primarily for web development, but in many ways sucks at this.

Page 244: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

PSR-7 provides a common set of interfaces for HTTP messages (both request and response).

Page 245: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

PSR-7 RFCs

• HTTP Messages as described in RFC 7230 and RFC 7231

• URIs as described in RFC 3986

Page 246: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

PHP uses HTTP Request messages in two ways

Page 247: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

1. Sending an HTTP request

Via cURL, stream layer, or some other means.

Page 248: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

#2 Process an incoming HTTP request.

A user makes a request to a web page that is powered and processed by PHP.

Page 249: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Sending a request

Page 250: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

PHP does not have built-in support for HTTP

messages.

Page 251: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

You must use an add-on for HTTP requests

Page 252: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Mechanisms for requests

• PHP Streams

• The cURL extension

• ext/http

Page 253: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Streams are most common (and built into

PHP)

Page 254: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

cURL is available, powerful, but not built-in.

Page 255: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Same with ext/http. (Plus, no examples)

Page 256: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Most modern libraries making HTTP requests use abstraction so they can use any available HTTP

sending mechanism.

Page 257: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Receiving a request

Page 258: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

PHP’s SAPI processes query strings and bodies into $_GET and $_POST.

Page 259: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

It also takes care of processing cookies, files

and server variables.

Page 260: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

A history of PHP development

Page 261: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

First, PHP was a templating language.

Page 262: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Superglobals were used everywhere, because that

was all we had.

Page 263: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

PHP 4 came along and gave us objects.

Page 264: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

PHP 5 came along and gave us better objects.

Page 265: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Objects begat object-oriented frameworks.

Page 266: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Frameworks began defining their own HTTP

components.

Page 267: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

PSR-0 standardized autoloading (+ PSR-4)

Page 268: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Composer made library building and distribution

easy.

Page 269: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Libraries for HTTP began to proliferate.

Page 270: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

The PHP-FIG saw a need and created PSR-7.

Page 271: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Why PSR-7?

Page 272: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Multiple HTTP implementations is

confusing and wasteful.

Page 273: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Different HTTP specifications makes

interoperability impossible.

Page 274: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Direct use of superglobals is considered harmful.

Page 275: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

So the PHP-FIG set out to abstract both client- and server-side

request and response interfaces.

Page 276: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Interoperability

Page 277: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

So what did FIG actually do?

Page 278: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Interfaces

Page 279: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

PSR-7 Interfaces• Psr\Http\Message\MessageInterface;

• Psr\Http\Message\RequestInterface;

• Psr\Http\Message\ServerRequestInterface;

• Psr\Http\Message\ResponseInterface;

• Psr\Http\Message\StreamInterface;

• Psr\Http\Message\UriInterface;

• Psr\Http\Message\UploadedFileInterface;

Page 280: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

The MessageInterface

Page 281: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

RequestInterface and ResponseInterface extend

from MessageInterface

Page 282: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

URI’s as objects

Page 283: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Immutability

Page 284: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Immutability is the property of being unchangeable. Whenever an immutable

request/response object is “modified”, the return value is a brand new object with the

requested modification. The original remains the same.

Page 285: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Streams

Page 286: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Streams can be hard to work with (StreamInterface

seeks to solve for this)

Page 287: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Streams are mutable!

Page 288: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Stream immutability is impossible to enforce.

Page 289: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Why is there a ServerRequestInterface?

Page 290: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Server-side requests have special needs.

Page 291: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Access to $_GET, $_POST, $_SERVER,

$_COOKIE and $_FILES.

Page 292: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Access to the URI for routing.

Page 293: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Notice: the return value of ServerRequestInterface::

getParsedBody() is indeterminate.

Page 294: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Files are objects.

Page 295: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

This fixes several problems with PHP.

Page 296: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

array( 'files' => array( 'name' => array( 0 => 'file0.txt', 1 => 'file1.html', ), 'type' => array( 0 => 'text/plain', 1 => 'text/html', ), /* etc. */ ), )

Page 297: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

PHP won’t process files not in a POST request

(think PUT).

Page 298: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

PSR-15 HTTP Server Request Handlers

Page 299: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

PSR-15 defines a standard for creating reusable middleware.

Page 300: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

PSR-15 extends the PSR-7 concept by integrating it

directly into the middleware.

Page 301: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Approaches to Middleware Design

Page 302: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

The “Double Pass”

<?php

public function handle( ServerRequestInterface $request, ResponseInterface $response, callable $next ) : ResponseInterface;

Page 303: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

The “Single Pass” (Lambda)

<?php

public function handle( ServerRequestInterface $request, callable $next ) : ResponseInterface;

Page 304: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Middleware has no access to a response until one is generated

by the request handler.

Page 305: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

PSR-15 uses the single pass.

Page 306: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.
Page 307: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

The RequestHandlerInterface

<?php

namespace Psr\Http\Server;

use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface;

interface RequestHandlerInterface { public function handle(

ServerRequestInterface $request): ResponseInterface; }

Page 308: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

The “end of the line.”

Page 309: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

RequestHandlerInterface MUST return a response.

Page 310: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

The MiddlewareInterface

<?php

namespace Psr\Http\Server;

use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface;

interface MiddlewareInterface { public function process(

ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface;

}

Page 311: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

MUST return a response (but may delegate)

Page 312: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions.

Questions?

Brandon Savage [email protected] @brandonsavage on Twitter