22
Inversion of Control - что такое, подходы, Service Locator, DI, простые примеры

Benefits of unit-testing and inversion of controll

  • Upload
    -

  • View
    182

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Benefits of unit-testing and inversion of controll

Inversion of Control - что такое, подходы, Service Locator, DI, простые

примеры

Page 2: Benefits of unit-testing and inversion of controll

О пользе тестирования

Жизнь программиста без тестирования

● Пишет много кода и однажды... он перестает работать● Исправление ошибки может быть легкими не замысловатым, но процесс нахождения

ошибки может затягиваться:○ часы, потраченные на отладку и изучение вывода дампов в консоль

● Исправление ошибки может поломать существующие в коде зависимости● Ошибка может проявиться снова на более позднем этапе

В итоге: время разработки и поддержки кода растет с возрастом проекта

Page 3: Benefits of unit-testing and inversion of controll

О пользе тестирования

Почему разработчики не пишут тесты

Оправдания Причины

● это работает на моем компьютере● предыдущий разработчик не знал об

blah-blah-blah● не могу воспроизвести ошибку● когда я это делал было не так...

● нет времени● бюджет не предусматривает● разработчики не знают как писать тесты● написание тестов после релиза

Page 4: Benefits of unit-testing and inversion of controll

О пользе тестирования

"Once a test is made, it will always be tested"Michelangelo van Dam

кто-то на MageConf 2012

"Мало плохих тестов лучше, чем вообще без них"

"Every time you wish to dump a variable, write a unit-test for this case."

Chris Hartjes (The Grumpy programmer)

Page 5: Benefits of unit-testing and inversion of controll

О пользе тестирования

Преимущества тестирования

Поддержка существующего кода Уверенность

● В процессе разработки○ тесты не пройдут, если допущена

ошибка в месте которое они покрывают

● После релиза○ можно быстро узнать, является ли

баг полинным или его "сочинили"○ решение проблемных вопросов не

ломает зависимостей в существующем коде■ если ломает, то тесты фейлятся и

сразу видно где нужно исправить● Долгосрочные проекты

○ упрощает процесс рефакторинга

● Для разработчика○ его код работает○ тесты дают базовое понимание

работы API приложения, упрощают процесс создания документации

● Для менеджера○ проект удачен

● Для продаж○ прибыль

● Для клиента○ доволен, т.к. получает то за что

платит

Page 6: Benefits of unit-testing and inversion of controll

Unit-тестирование (модульное тестировани) - это автоматизированная, самопроверяющая процедура, которая отвечает за правильность работы модулей.

Модули обычно ассоциируются с классами и задача unit-тестов сводится к проверки на корректность работы интерфейсных (или просто общедоступных) методов модуля (класса).

Проверка класса должна происходить в его изоляции от приложения.

О пользе тестирования

Что есть unit-тестирование

Тест не является модульным, если:

● тест общается с базой данных● тест общается с сетью● тест касается файловой системы● тест запускается одновременно с другим unit-тестом● для его запуска нужно переконфигурировать приложение● тест не тестируется в изоляции от других классов

Page 7: Benefits of unit-testing and inversion of controll

О пользе тестирования

С чего все началось

Началу эры тестирования положил Кент Бек в своей книге "Extreme programming Explained", в которой он так же изложил первые принцыпы Test-Driven Development (TDD) (разработка через тестирование). Основная мысль автора заключалась в следующем: если тестиование это хорошо, значит программисты должны постоянно тестирвать свой код.

Набор рекомендаций по правилам unit-тестирования и составляет основу методологии TDD.

Одно из определений TDD гласит - что TDD это методика, позволяющая оптимизировать использование модульных тестов.

Задача TDD - достижение балланса между усилиями и результатом.

Page 8: Benefits of unit-testing and inversion of controll

О пользе тестирования

Итерация в TDD

Тестирование

Проектирование

Реализация / Рефакторинг

ТестированиеТестирование Тестирование

Page 9: Benefits of unit-testing and inversion of controll

О пользе тестирования

Цена ошибок без unit-тестов

- кол-во багов - траты на проект

запрос на изменение 1

запрос на изменение 2

запрос на изменение N

врем

я на

вне

дрен

ие н

овой

ичи"

жизнь проекта

коли

чест

во б

агов

- время на новую фичу

жизнь проекта

Page 10: Benefits of unit-testing and inversion of controll

О пользе тестирования

Цена ошибок c unit-тестами

- кол-во багов - траты на проект

запрос на изменение 1

запрос на изменение 2

запрос на изменение N

врем

я на

вне

дрен

ие н

овой

ичи"

жизнь проекта

коли

чест

во б

агов

и н

овы

х те

стов

- время на новую фичу

жизнь проекта

- unit-тесты

Page 11: Benefits of unit-testing and inversion of controll

О пользе тестирования

Скорость разработки приложения

- приект без unit-тестов

врем

я на

вне

дрен

ие н

овог

о ф

ункц

иона

ла

жизнь проекта

- проект с unit-тестами

Page 12: Benefits of unit-testing and inversion of controll

О пользе тестирования

Проблема модульного тестирования

● Черезмерное покрытие тестами:○ не стоит увлекаться в написании модульных тестов, достаточно что бы они покрывали,

написанную Вами, бизнес логику приложения

● Тесты должны быть простыми:○ тесты, на написание которых у Вас уходит очень много времени - плохие, свозможно стоит их

разбить на несколько тестов или убрать "лишний" функционал из, предварительно разработанного Вами дизайна модуля

● Простота написания тестов зависит от архитектуры Вашего приложения○ чем больше жестких зависимостей, тем хуже

Page 13: Benefits of unit-testing and inversion of controll

Зависимости

Откуда берутся зависимости

Приложение

PHP extensions / Utils

Массивы данных Ваши классы

уровень контроллеров

уровень сервисов и моделей

з а в и с и м о с т и

Page 14: Benefits of unit-testing and inversion of controll

Зависимости

Зависимости это плохо ?

"Hard-coded dependencies are bad"Stephan Hachdörfer

Создание жестких зависимостей в классах делает их (классы) не изолированными (ортогональными) и, как следствие, приложение становится трудно-тестируемым.

Тут на помощь приходит техника программирования - Inversion of Control.

Обычно, объекты зависят друг от друга, и это нормальная ситуация. В ООП благородным делом считается избавление объектов от излишних зависимостей, в то же время вообще уйти от них - невозможно! На фоне этого основной задачей разработчика становится контроль зависимостей и их разумное уменьшение.

Page 15: Benefits of unit-testing and inversion of controll

Inversion of Control

Определения

Inversion of Control - это техника объектно-ориентированного программирования, которая используется для устранение жестких зависимостей в коде, делая компоненты приложения многоразовыми. Так же входит в состав SOLID-принцыпов, которые лежат в основе TDD.

Техника релизует создание объектов "налету" с помощью специального контейнера, определяющего граф зависимостей кассов приложения, и предоставляющего возможность устанавливать зависимости между объектами, которые не доступны на этапе компиляции или при статическом анализе.

Существует несколько техник применения Инверсии зависимостей:● Factory method pattern● Service locator pattern● Dependency injection pattern

○ инъекция в constructor○ инъекция через setter method○ инъекция через интерфейс

● контекстный поиск

Page 16: Benefits of unit-testing and inversion of controll

Inversion of Control

Service Locator Pattern

диаграмма взята из MSDN

использует

использует

ClassA

ServiceA

ServiceB

испо

льзу

етлокализирует

Locator

ServiceA

ServiceB

ClassA

локализирует

Начальная ситуация● что бы изменить зависимости - нужно

изменить код● зависимости должны быть при компиляции● трудно протестировать класс в изоляции● повторяемый код на создание,

локализацию и управление зависимостями

Применение Шаблона Service Locator● декомпозиция класса● класс не должен ничего знать о сервисах● класс можно протестировать в изоляции● нет логики управления зависимостями

внутри класса● приложение становится модульным,

каждый модуль независим

Page 17: Benefits of unit-testing and inversion of controll

Inversion of Control

Factory Method Pattern

использует

использует

ClassA

ServiceA

ServiceB

используетFactory

ServiceA ServiceB

ClassA

Начальная ситуация● что бы изменить зависимости - нужно

изменить код● зависимости должны быть при компиляции● трудно протестировать класс в изоляции● повторяемый код на создание,

локализацию и управление зависимостями

Применение Шаблона Factory Method● декомпозиция класса● класс не должен ничего знать о сервисах● класс можно протестировать в изоляции● нет логики управления зависимостями

внутри класса● приложение становится модульным,

каждый модуль независим

фабричный метод

Page 18: Benefits of unit-testing and inversion of controll

Inversion of Control

Dependency Injection Pattern

диаграмма взята из MSDN

использует

использует

ClassA

ServiceA

ServiceB

создаетBuilder

ServiceA

ClassA

Начальная ситуация● что бы изменить зависимости - нужно

изменить код● зависимости должны быть при компиляции● трудно протестировать класс в изоляции● повторяемый код на создание,

локализацию и управление зависимостями

Применение Шаблона Factory Method● декомпозиция класса● класс не должен ничего знать о сервисах● класс можно протестировать в изоляции● нет логики управления зависимостями

внутри класса● приложение становится модульным,

каждый модуль независим

инстанциируетвнедряет

IServiceA

использует

Пример использования интерфейсной инъекции с помощью Factory Method

Page 19: Benefits of unit-testing and inversion of controll

Inversion of Control

Простейший пример Dependency Injection (Di)

Базовый класс Отрефакторен с использованием Di

class Foo{ protected $_bar;

public function __construct() { $this->bar = new Bar() }}

class Foo{ protected $_bar;

// инъекция через конструктор public function __construct(Bar $bar) { $this->bar = $bar; }

// инъекция через setter public function setBar(Bar $bar) { $this->bar = $bar; }}

Page 20: Benefits of unit-testing and inversion of controll

Dependency Injection in frameworks

ZendFramework 2 :: базовый пример

Описание зависимостей Применение Di контейнера

$definitions = array( 'Foo' => array( 'setBar' => array( 'type' => 'Bar', 'required' => true, ) ));

use Zend\Di\Di, Zend\Di\Configuration;

$di = new Di();$config = new Configuration(array( 'definition' => array('class' => $definition)));

$foo = $di->get('Foo');

Больше и более детальные примеры можно посмтреть тут:https://github.com/ralphschindler/Zend_DI-Examples/

Page 21: Benefits of unit-testing and inversion of controll

Dependency Injection in frameworks

ZendFramework 2 :: Martin Fawler's Movie & Lister

namespace MovieApp {

class Lister { public $dbFinder; public function __construct(DbFinder $dbFinder){ $this->dbFinder = $dbFinder; } }

class DbFinder { public $username, $password = null; public function __construct($username, $password) { $this->username = $username; $this->password = $password; } }

}

namespace { // bootstrap Zend\Loader\StandardAutoloader first

$di = new Zend\Di\Di; $di->instanceManager()->setParameters( 'MovieApp\DbFinder', array( 'username' => 'my-username', 'password' => 'my-password' ) ); $lister = $di->get('MovieApp\Lister');

$works = ( $lister->dbFinder instanceof MovieApp\DbFinder && $lister->dbFinder->username == 'my-username' && $lister->dbFinder->password == 'my-password' );

echo (($works) ? 'Works!' : 'Fails') . PHP_EOL; }

Page 22: Benefits of unit-testing and inversion of controll

Dependency Injection in frameworks

Symfony 2

class Mailer{ private $transport;

public function __construct() { $this->transport = 'sendmail'; }

// ...}

class NewsletterManager{ private $mailer;

public function __construct(\Mailer $mailer) { $this->mailer = $mailer; }

// ...}

use Symfony\Component\DependencyInjection\ContainerBuilder;use Symfony\Component\DependencyInjection\Reference;

$container = new ContainerBuilder();

$container->setParameter('mailer.transport', 'sendmail');$container ->register('mailer', 'Mailer') ->addArgument('%mailer.transport%');

$container ->register('newsletter_manager', 'NewsletterManager') ->addArgument(new Reference('mailer'));