dependency injection di - php-schulung.de€¦ · dependency injection. timon schroeter 2...

27
Timon Schroeter 1 www.php-schulung.de Dependency Injection DI

Upload: others

Post on 03-Aug-2020

17 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Dependency Injection DI - php-schulung.de€¦ · Dependency Injection. Timon Schroeter 2 Specificity Stability Unit Tests Acceptance Tests Functional Tests Integration Tests Not

Timon Schroeter 1 www.php-schulung.de

Dependency Injection

DI

Page 2: Dependency Injection DI - php-schulung.de€¦ · Dependency Injection. Timon Schroeter 2 Specificity Stability Unit Tests Acceptance Tests Functional Tests Integration Tests Not

Timon Schroeter 2 www.php-schulung.de

Specificity

Stability

UnitTests

AcceptanceTests

FunctionalTests

IntegrationTests

Not all tests are created equal

Page 3: Dependency Injection DI - php-schulung.de€¦ · Dependency Injection. Timon Schroeter 2 Specificity Stability Unit Tests Acceptance Tests Functional Tests Integration Tests Not

Timon Schroeter 3 www.php-schulung.de

Specificity

Stability

UnitTests

AcceptanceTests

FunctionalTests

IntegrationTests

Most valuable,require high qualitywell structured code

Not all tests are created equal

Page 4: Dependency Injection DI - php-schulung.de€¦ · Dependency Injection. Timon Schroeter 2 Specificity Stability Unit Tests Acceptance Tests Functional Tests Integration Tests Not

Timon Schroeter 4 www.php-schulung.de

Real Unit Tests

● Test a single unit of code, i.e. a single class

Page 5: Dependency Injection DI - php-schulung.de€¦ · Dependency Injection. Timon Schroeter 2 Specificity Stability Unit Tests Acceptance Tests Functional Tests Integration Tests Not

Timon Schroeter 5 www.php-schulung.de

Real Unit Tests

● Test a single unit of code, i.e. a single class

We need to be able toreplace (mock/stub)

dependencies dynamically

Page 6: Dependency Injection DI - php-schulung.de€¦ · Dependency Injection. Timon Schroeter 2 Specificity Stability Unit Tests Acceptance Tests Functional Tests Integration Tests Not

Timon Schroeter 6 www.php-schulung.de

Structure of this presentation

● Why do we want Dependency Injection?● Code example: DI for generic PHP classes● Code example: DI in Symfony 2

You are here

Page 7: Dependency Injection DI - php-schulung.de€¦ · Dependency Injection. Timon Schroeter 2 Specificity Stability Unit Tests Acceptance Tests Functional Tests Integration Tests Not

Timon Schroeter 7 www.php-schulung.de

<?phpuse Guzzle\Http\Client;use Acme\Logger\XmlLogger;

class FeedAggregator {private $client;private $logger;

public function __construct () {$this->client = new Client();$this->logger = new XmlLogger();

}

public function retrieveFeed ($baseurl, $path) {$request = $this->client->setBaseUrl($baseurl)->get($path);$response = $request->send();if (200 != $response->getStatusCode()) {

$this->logger->log('Could not get: '.$host.$path);return null;

}

return $response->getBody();}// ...

}

Why is this classdifficult to unit test?

Page 8: Dependency Injection DI - php-schulung.de€¦ · Dependency Injection. Timon Schroeter 2 Specificity Stability Unit Tests Acceptance Tests Functional Tests Integration Tests Not

Timon Schroeter 8 www.php-schulung.de

<?phpuse Guzzle\Http\Client;use Acme\Logger\XmlLogger;

class FeedAggregator {private $client;private $logger;

public function __construct () {$this->client = new Client();$this->logger = new XmlLogger();

}

public function retrieveFeed ($baseurl, $path) {$request = $this->client->setBaseUrl($baseurl)->get($path);$response = $request->send();if (200 != $response->getStatusCode()) {

$this->logger->log('Could not get: '.$host.$path);return null;

}

return $response->getBody();}// ...

}

What if we want unit tests to run fastwithout waiting for the network?

Why is this classdifficult to unit test?

Page 9: Dependency Injection DI - php-schulung.de€¦ · Dependency Injection. Timon Schroeter 2 Specificity Stability Unit Tests Acceptance Tests Functional Tests Integration Tests Not

Timon Schroeter 9 www.php-schulung.de

<?phpuse Guzzle\Http\Client;use Acme\Logger\XmlLogger;

class FeedAggregator {private $client;private $logger;

public function __construct () {$this->client = new Client();$this->logger = new XmlLogger();

}

public function retrieveFeed ($baseurl, $path) {$request = $this->client->setBaseUrl($baseurl)->get($path);$response = $request->send();if (200 != $response->getStatusCode()) {

$this->logger->log('Could not get: '.$host.$path);return null;

}

return $response->getBody();}// ...

}

What if we want unit tests to run fastwithout waiting for the network?

What if we want unit tests to run fastwithout logging?

What if we want unit tests to run fastwithout logging?

Why is this classdifficult to unit test?

Page 10: Dependency Injection DI - php-schulung.de€¦ · Dependency Injection. Timon Schroeter 2 Specificity Stability Unit Tests Acceptance Tests Functional Tests Integration Tests Not

Timon Schroeter 10 www.php-schulung.de

<?phpuse Guzzle\Http\Client;use Acme\Logger\XmlLogger;

class FeedAggregator {private $client;private $logger;

public function __construct () {$this->client = new Client();$this->logger = new XmlLogger();

}

public function retrieveFeed ($baseurl, $path) {$request = $this->client->setBaseUrl($baseurl)->get($path);$response = $request->send();if (200 != $response->getStatusCode()) {

$this->logger->log('Could not get: '.$host.$path);return null;

}

return $response->getBody();}// ...

}

What if we want unit tests to run fastwithout waiting for the network?

What if we ever want to

use a different HTTP client?

What if we want unit tests to run fastwithout logging?

What if we want unit tests to run fastwithout logging?

Why is this classdifficult to unit test?

Page 11: Dependency Injection DI - php-schulung.de€¦ · Dependency Injection. Timon Schroeter 2 Specificity Stability Unit Tests Acceptance Tests Functional Tests Integration Tests Not

Timon Schroeter 11 www.php-schulung.de

<?phpuse Guzzle\Http\Client;use Acme\Logger\XmlLogger;

class FeedAggregator {private $client;private $logger;

public function __construct () {$this->client = new Client();$this->logger = new XmlLogger();

}

public function retrieveFeed ($baseurl, $path) {$request = $this->client->setBaseUrl($baseurl)->get($path);$response = $request->send();if (200 != $response->getStatusCode()) {

$this->logger->log('Could not get: '.$host.$path);return null;

}

return $response->getBody();}// ...

}

What if we want unit tests to run fastwithout waiting for the network?

What if we ever want to

use a different HTTP client?

What if we want unit tests to run fastwithout logging?

What if we ever want to

use a different logger class?

What if we want unit tests to run fastwithout logging?

Why is this classdifficult to unit test?

Page 12: Dependency Injection DI - php-schulung.de€¦ · Dependency Injection. Timon Schroeter 2 Specificity Stability Unit Tests Acceptance Tests Functional Tests Integration Tests Not

Timon Schroeter 12 www.php-schulung.de

<?phpuse Guzzle\Http\Client;use Acme\Logger\XmlLogger;

class FeedAggregator {private $client;private $logger;

public function __construct () {$this->client = new Client();$this->logger = new XmlLogger();

}

public function retrieveFeed ($baseurl, $path) {$request = $this->client->setBaseUrl($baseurl)->get($path);$response = $request->send();if (200 != $response->getStatusCode()) {

$this->logger->log('Could not get: '.$host.$path);return null;

}

return $response->getBody();}// ...

}

What if we want unit tests to run fastwithout waiting for the network?

What if we ever want to

use a different HTTP client?

What if we want unit tests to run fastwithout logging?

What if we ever want to

use a different logger class?

What if we ever want to

use a different log format?

What if we want unit tests to run fastwithout logging?

Why is this classdifficult to unit test?

Page 13: Dependency Injection DI - php-schulung.de€¦ · Dependency Injection. Timon Schroeter 2 Specificity Stability Unit Tests Acceptance Tests Functional Tests Integration Tests Not

Timon Schroeter 13 www.php-schulung.de

<?phpuse Guzzle\Http\Client;use Acme\Logger\XmlLogger;

class FeedAggregator {private $client;private $logger;

public function __construct () {$this->client = new Client();$this->logger = new XmlLogger();

}

public function retrieveFeed ($baseurl, $path) {$request = $this->client->setBaseUrl($baseurl)->get($path);$response = $request->send();if (200 != $response->getStatusCode()) {

$this->logger->log('Could not get: '.$host.$path);return null;

}

return $response->getBody();}// ...

}

What if we want unit tests to run fastwithout waiting for the network?

What if we ever want to

use a different HTTP client?

What if we want unit tests to run fastwithout logging?

What if we ever want to

use a different logger class?

What if we ever want to

use a different log format?

What if we want unit tests to run fastwithout logging?

Why is this classdifficult to unit test?

Dependencies are pulled.=> Replacing requires refactoring=> Dynamic replacing (only for

testing) is impossible

Page 14: Dependency Injection DI - php-schulung.de€¦ · Dependency Injection. Timon Schroeter 2 Specificity Stability Unit Tests Acceptance Tests Functional Tests Integration Tests Not

Timon Schroeter 14 www.php-schulung.de

<?phpuse Guzzle\Http\Client;use Acme\Logger\XmlLogger;

class FeedAggregator {private $client;private $logger;

public function __construct () {

$this->client = new Client();$this->logger = new XmlLogger();

}

public function retrieveFeed ($baseurl, $path) {$request = $this->client->setBaseUrl($baseurl)->get($path);$response = $request->send();if (200 != $response->getStatusCode()) {

$this->logger->log('Could not get: '.$host.$path);return null;

}

return $response->getBody();}// ...

}

Dependencies are pulled.

Page 15: Dependency Injection DI - php-schulung.de€¦ · Dependency Injection. Timon Schroeter 2 Specificity Stability Unit Tests Acceptance Tests Functional Tests Integration Tests Not

Timon Schroeter 15 www.php-schulung.de

<?phpuse Guzzle\Http\ClientInterface;use Acme\Logger\LoggerInterface;

class FeedAggregator {private $client;private $logger;

public function __construct(ClientInterface $client, LoggerInterface $logger) {

$this->client = $client;$this->logger = $logger;

}

public function retrieveFeed ($baseurl, $path) {$request = $this->client->setBaseUrl($baseurl)->get($path);$response = $request->send();if (200 != $response->getStatusCode()) {

$this->logger->log('Could not get: '.$host.$path);return null;

}

return $response->getBody();}// ...

}

Dependencies are pushed.

Page 16: Dependency Injection DI - php-schulung.de€¦ · Dependency Injection. Timon Schroeter 2 Specificity Stability Unit Tests Acceptance Tests Functional Tests Integration Tests Not

Timon Schroeter 16 www.php-schulung.de

<?phpuse Guzzle\Http\ClientInterface;use Acme\Logger\LoggerInterface;

class FeedAggregator {private $client;private $logger;

public function __construct(ClientInterface $client, LoggerInterface $logger) {

$this->client = $client;$this->logger = $logger;

}

public function retrieveFeed ($baseurl, $path) {$request = $this->client->setBaseUrl($baseurl)->get($path);$response = $request->send();if (200 != $response->getStatusCode()) {

$this->logger->log('Could not get: '.$host.$path);return null;

}

return $response->getBody();}// ...

}

Dependencies are pushed.

Class only dependson interfaces

Page 17: Dependency Injection DI - php-schulung.de€¦ · Dependency Injection. Timon Schroeter 2 Specificity Stability Unit Tests Acceptance Tests Functional Tests Integration Tests Not

Timon Schroeter 17 www.php-schulung.de

<?phpuse Guzzle\Http\ClientInterface;use Acme\Logger\LoggerInterface;

class FeedAggregator {private $client;private $logger;

public function __construct(ClientInterface $client, LoggerInterface $logger) {

$this->client = $client;$this->logger = $logger;

}

public function retrieveFeed ($baseurl, $path) {$request = $this->client->setBaseUrl($baseurl)->get($path);$response = $request->send();if (200 != $response->getStatusCode()) {

$this->logger->log('Could not get: '.$host.$path);return null;

}

return $response->getBody();}// ...

}

Dependencies are pushed.

Class only dependson interfaces

Implementations areinjected at runtime

Page 18: Dependency Injection DI - php-schulung.de€¦ · Dependency Injection. Timon Schroeter 2 Specificity Stability Unit Tests Acceptance Tests Functional Tests Integration Tests Not

Timon Schroeter 18 www.php-schulung.de

<?phpuse Guzzle\Http\ClientInterface;use Acme\Logger\LoggerInterface;

class FeedAggregator {private $client;private $logger;

public function __construct(ClientInterface $client, LoggerInterface $logger) {

$this->client = $client;$this->logger = $logger;

}

public function retrieveFeed ($baseurl, $path) {$request = $this->client->setBaseUrl($baseurl)->get($path);$response = $request->send();if (200 != $response->getStatusCode()) {

$this->logger->log('Could not get: '.$host.$path);return null;

}

return $response->getBody();}// ...

}

Dependencies are pushed.

Class only dependson interfaces

Implementations areinjected at runtime

Easy to replace, evendynamically (for testing)

Page 19: Dependency Injection DI - php-schulung.de€¦ · Dependency Injection. Timon Schroeter 2 Specificity Stability Unit Tests Acceptance Tests Functional Tests Integration Tests Not

Timon Schroeter 19 www.php-schulung.de

<?phpuse Guzzle\Http\ClientInterface;use Acme\Logger\LoggerInterface;

class FeedAggregator {private $client;private $logger;

public function __construct(ClientInterface $client, LoggerInterface $logger) {

$this->client = $client;$this->logger = $logger;

}

public function retrieveFeed ($baseurl, $path) {$request = $this->client->setBaseUrl($baseurl)->get($path);$response = $request->send();if (200 != $response->getStatusCode()) {

$this->logger->log('Could not get: '.$host.$path);return null;

}

return $response->getBody();}// ...

}

Dependencies are pushed.

Class only dependson interfaces

Implementations areinjected at runtime

Easy to replace, evendynamically (for testing)

On the level of the class,You are now experts for Dependency Injection.

Page 20: Dependency Injection DI - php-schulung.de€¦ · Dependency Injection. Timon Schroeter 2 Specificity Stability Unit Tests Acceptance Tests Functional Tests Integration Tests Not

Timon Schroeter 20 www.php-schulung.de

<?phpuse Guzzle\Http\ClientInterface;use Acme\Logger\LoggerInterface;

class FeedAggregator {private $client;private $logger;

public function __construct(ClientInterface $client, LoggerInterface $logger) {

$this->client = $client;$this->logger = $logger;

}

public function retrieveFeed ($baseurl, $path) {$request = $this->client->setBaseUrl($baseurl)->get($path);$response = $request->send();if (200 != $response->getStatusCode()) {

$this->logger->log('Could not get: '.$host.$path);return null;

}

return $response->getBody();}// ...

}

Dependencies are pushed.

Class only dependson interfaces

Implementations areinjected at runtime

Easy to replace, evendynamically (for testing)

On the level of the class,You are now experts for Dependency Injection.

Any questions?

Page 21: Dependency Injection DI - php-schulung.de€¦ · Dependency Injection. Timon Schroeter 2 Specificity Stability Unit Tests Acceptance Tests Functional Tests Integration Tests Not

Timon Schroeter 21 www.php-schulung.de

<?phpuse Guzzle\Http\ClientInterface;use Acme\Logger\LoggerInterface;

class FeedAggregator {private $client;private $logger;

public function __construct(ClientInterface $client, LoggerInterface $logger) {

$this->client = $client;$this->logger = $logger;

}

public function retrieveFeed ($baseurl, $path) {$request = $this->client->setBaseUrl($baseurl)->get($path);$response = $request->send();if (200 != $response->getStatusCode()) {

$this->logger->log('Could not get: '.$host.$path);return null;

}

return $response->getBody();}// ...

}

Dependencies are pushed.

Class only dependson interfaces

Implementations areinjected at runtime

Easy to replace, evendynamically (for testing)

On the level of the class,You are now experts for Dependency Injection.

Who constructsand pushes all the

dependencies?

Page 22: Dependency Injection DI - php-schulung.de€¦ · Dependency Injection. Timon Schroeter 2 Specificity Stability Unit Tests Acceptance Tests Functional Tests Integration Tests Not

Timon Schroeter 22 www.php-schulung.de

Page 23: Dependency Injection DI - php-schulung.de€¦ · Dependency Injection. Timon Schroeter 2 Specificity Stability Unit Tests Acceptance Tests Functional Tests Integration Tests Not

Timon Schroeter 23 www.php-schulung.de

Dependency Injection Container

DIC

“DI Container”, “DIC”, “Service Container”, “the Container”

Page 24: Dependency Injection DI - php-schulung.de€¦ · Dependency Injection. Timon Schroeter 2 Specificity Stability Unit Tests Acceptance Tests Functional Tests Integration Tests Not

Timon Schroeter 24 www.php-schulung.de

Page 25: Dependency Injection DI - php-schulung.de€¦ · Dependency Injection. Timon Schroeter 2 Specificity Stability Unit Tests Acceptance Tests Functional Tests Integration Tests Not

Timon Schroeter 25 www.php-schulung.de

Very Many Frameworks support Dependency Injection

Page 26: Dependency Injection DI - php-schulung.de€¦ · Dependency Injection. Timon Schroeter 2 Specificity Stability Unit Tests Acceptance Tests Functional Tests Integration Tests Not

Timon Schroeter 26 www.php-schulung.de

Vielen Dank für Eure Aufmerksamkeit!

Page 27: Dependency Injection DI - php-schulung.de€¦ · Dependency Injection. Timon Schroeter 2 Specificity Stability Unit Tests Acceptance Tests Functional Tests Integration Tests Not

Timon Schroeter 27 www.php-schulung.de

SOLID

● S Single Responsibility Principle● O Open / Close Principle● L Liskov Substitution Principle● I Interface Segregation Principle● D Dependency Inversion Principle