4. Why change is hard?
“Object-oriented design is about managing dependencies. It is a set of coding
techniques that arrange dependencies such that objects can tolerate change.
In the absence of design, unmanaged dependencies wreak havoc because
objects know too much about one another. Changing one object forces change
upon its collaborators, which in turn, forces change upon its collaborators, ad
infinitum. A seemingly insignificant enhancement can cause damage that
radiates outward in overlapping concentric circles, ultimately leaving no code
untouched.
When objects know too much they have many expectations about the world in
which they reside. They’re picky, they need things to be “just so.” These
expectations constrain them. The objects resist being reused in different
contexts; they are painful to test and susceptible to being duplicated.”
Sandi Metz. “Practical Object-Oriented Design in Ruby: An Agile Primer (Jason Arnold's Library).”
5. “Because well designed objects have a single
responsibility, their very nature requires that they
collaborate to accomplish complex tasks. This collaboration
is powerful and perilous. To collaborate, an object must
know something know about others. Knowing creates a
dependency. If not managed carefully, these dependencies
will strangle your application.”
Sandi Metz. “Practical Object-Oriented Design in Ruby: An Agile Primer (Jason Arnold's Library).”
7. Depenency Injection - Definition
« Dependency Injection is where components are given
their dependencies through their constructors, methods, or
directly into fields. »
8. Why DI ?
Goal: we want to write code that is...
✔ Clutter-free
✔ Reusable
✔ Testable
9. An injection is the passing of a dependency (a service) to a
dependent object (a client).
The service is made part of the client's state. Passing the
service to the client, rather than allowing a client to build or
find the service, is the fundamental requirement of the
pattern.
10. What are dependencies?
● framework
● third party libraries
● database
● filesystem
● email
● web services
● system resources (Clock)
● configuration
● the new keyword
● static methods
● random
11. Before DI
class A
{
public function a1() {
$b = new B();
$b->b1();
}
}
12. class Flashcard
{
private $db;
public function __construct()
{
hard to customize
$this->db = new new MysqlConnection('flashcards');
}
}
$flashcard = new Flashcard();
easy to use
13. Types of DI
- constructor injection
- setter injection
- interface injection
- property injection
14. DI - Constructor Injection
class Flashcard
{
private $db;
public function __construct(MySqlConnection $db)
{
$this->db = $db;
}
}
$db = new MySQLConnection();
$flashcard = new Flashcard($db);
slightly more difficult to use
15. DI - Setter Injection
class Flashcard
{
private $db;
public setDb(MySqlConnection $db)
{
$this->db = $db;
}
}
16. Dependency injection is a software design
pattern that implements inversion of control and
allows a program design to follow the
dependency inversion principle.
20. Dependency Inversion Principle
A. High-level modules should not depend on low-level modules.
Both should depend on abstractions.
B. Abstractions should not depend on details.
Details should depend on abstractions.
21. class Flashcard
{
private $db;
public function __construct(MySqlConnection $db)
{
$this->db = $db;
}
}
22. interface ConnectionInterface {
public function connect();
}
class DbConnection implements ConnectionInterface {
public function connect() {
//do something
};
}class Flashcard {
public function __construct(ConnectionInterface $connection){
$this->connection = $connection;
}
}
$db = new DbConnection();
$flashcard = new Flashcard($db);
24. Inversion of Control
A design in which custom-written portions of a computer program receive the
flow of control from a generic, reusable library.
A software architecture with this design inverts control as compared to
traditional procedural programming: in traditional programming, the custom
code that expresses the purpose of the program calls into reusable libraries to
take care of generic tasks, but with inversion of control, it is the reusable code
that calls into the custom, or task-specific, code.
25. IoC = Hollywood Principle
"don't call us, we'll call you."
Instead of your program running the system, the system runs your program.
26. Inversion of Control - Why?
● To decouple the execution of a task from implementation.
● To focus a module on the task it is designed for.
● To free modules from assumptions about how other
systems do what they do and instead rely on contracts.
● To prevent side effects when replacing a module.
29. DI - Problem
$transport = new
Zend_Mail_Transport_Smtp('smtp.gmail.com', array(
'auth' => 'login',
'username' => 'foo',
'password' => 'bar',
'ssl' => 'ssl',
'port' => 465,
));
$mailer = new Zend_Mail();
$mailer->setDefaultTransport($transport);
30. Dependency Injection Container
A Dependency Injection Container is an object that
knows how to instantiate and configure objects.
And to be able to do its job, it needs to knows about the
constructor arguments and the relationships between the
objects.
These objects are called Services.
31.
32. How does it work?
➔ Service keys map to service definitions
➔ Definitions specify which class to instantiate and what its
dependencies are
➔ Dependencies are specified as references to other
services (using service keys)
➔ $container->getService('some_service')
33. Service Container
➔ Assumes responsibility for constructing object graphs
(i.e. instantiating your classes with their dependencies)
➔ Uses configuration data to know how to do this
➔ Allows infrastructure logic to be kept separate from
application logic
34. What is Service?
A service is any PHP class that performs an action.
A service is an object that provides some kind of
globally useful functionality
“A service is an object, registered at the service container
under a certain id. ”
Matthias Noback. “A Year With Symfony.”
36. Examples of NOT Services
➔ Product
➔ Blog post
➔ Email message
37. Symfony DI Component
The DependencyInjection component allows you to standardize and
centralize the way objects are constructed in your application.
38. Symfony DI Component
● Default scope: container
● Can be configured in PHP, XML or YAML
● Can be “compiled” down to plain PHP
39. SDIC - Compiling the container
It's too expensive to parse configuration on
every request.
Parse once and put the result into a PHP class
that hardcodes a method for each service.
40. SDIC - Compiling the container
serialization.json:
class: DrupalComponentSerializationJson
class service_container extends Container {
public function getSerialization_JsonService()
{ return $this->services['serialization.json'] =
new DrupalComponentSerializationJson();
}
}
41. SDIC - Compiling the container
public function getStateSevice()
{ return $this->services['state'] =
new DrupalCoreStateState(
$this->get('keyvalue')
);
}
42. Some D8 Services
➔ The default DB connection ('database')
➔ The module handler ('module_handler')
➔ The HTTP request object ('request')
53. D8 - how to get service?
● Drupal::getContainer()->get('url_generator')
● Drupal::service('url_generator')
● Drupal::urlGenerator()
please don’t!
INJECT LIKE HELL
54. D8 - Service Implementation
namespace Drupaldemo;
class DemoService {
protected $demo_value;
public function __construct() {
$this->demo_value = 'Upchuk';
}
public function getDemoValue() { return $this-
>demo_value;
}
}
55. D8 - Controller
class DemoController extends ControllerBase {
public function demo() { return array(
'#markup' => t('Hello @value!', array('@value' => $this-
>demoService->getDemoValue())),
);
}
}
56. D8 - Controller
class DemoController extends ControllerBase {
protected $demoService;
public function __construct($demoService) {
$this->demoService = $demoService;
}
public static function create(ContainerInterface $container)
{ return new static(
$container->get('demo.demo_service')
);
}
}
57. D8 - DI for a form
class ExampleForm extends FormBase {
public function __construct(AccountInterface $account) {
$this->account = $account;
}
public static function create(ContainerInterface $container) {
// Instantiates this form class. return new static(
// Load the service required to construct this class.
$container->get('current_user')
);
}
}
58. D8 - Altering existing services, providing dynamic services
namespace Drupalmy_module;use DrupalCoreDependencyInjection
ContainerBuilder;use DrupalCoreDependencyInjection
ServiceProviderBase;class MyModuleServiceProvider extends
ServiceProviderBase {
public function alter(ContainerBuilder $container) {
// Overrides language_manager class to test domain language
negotiation.
$definition = $container->getDefinition('language_manager');
$definition->setClass('Drupallanguage_testLanguageTestManager');
}
}
61. Resources
● Inversion of Control Containers and the Dependency
Injection pattern
● Symfony Service Container
● The DependencyInjection Component
● Services and dependency injection in Drupal 8