"dependency injection. javascript.", Сергей Камардин, moscowjs 15
DESCRIPTION
Слайды доклада "Dependency Injection. JavaScript.", Сергей Камардин, MoscowJS 15TRANSCRIPT
Dependency Injection. Javascript.
Сергей Камардин
Задача из жизни
1. Разместить на карте зарегистрированных пользователей;
!
2
«JS Way»Locator.prototype.locateUsers = function(users) { // Инициалиация сервиса карт loadScript("..."); client.init(...); ! // Создание карты var map = new client.Map(...); ! // Отображение на карте маркеров users.forEach(...); }
3
Задача из жизни
1. Разместить на карте зарегистрированных пользователей;
2. Поменять поставщика карт;
4
Решение: АбстракцияAbstractMapService.prototype = { ! createMap: function() { throw new TypeError("Not implemented"); }, ! createMapMarker: function() { throw new TypeError("Not implemented"); } !}
5
ИмплементацияAbstractMapService.extend({ ! createMap: function(id, options) { // }, ! createMapMarker: function(map, options) { // } !});
6
Locator.prototype.locateUsers = function(users) { // Create map service // Could be an Fabric Method, Service Locator, DI var mapService = new ConcreteMapService(...); ! // Create map var map = mapService.createMap(...); ! // Run each users, making markers users.forEach(...); }
7
Single responsibility?Locator.prototype.locateUsers = function(users) { // Create map service // Could be an Fabric Method, Service Locator, DI var mapService = new ConcreteMapService(...); ! // Create map var map = mapService.createMap(...); ! // Run each users, making markers users.forEach(...); }
8
Проблемы
• Невозможно написать юнит тесты;
• Использование сервиса карт во многих местах в коде;
• Избыточная функциональность локатора;
• Reusability.
9
Задача из жизни
1. Разместить на карте зарегистрированных пользователей;
2. Поменять поставщика карт;
3. Перенести локатор в другой проект, как плагин.
10
ResultLocator.prototype = { // injection setMapService: function(mapService) { this.mapService = mapService; }, ! locateUsers: function(users) { // Create map var map = this.mapService.createMap(...); ! // Run each users, making markers users.forEach(...); } }
11
12
ResultLocator.prototype = { // injection setMapService: function(mapService) { this.mapService = mapService; }, ! locateUsers: function(users) { // Create map var map = this.mapService.createMap(...); ! // Run each users, making markers users.forEach(...); } }
13
Вот оно
Routine// Create MapService var mapService = new MapService(...); !// Create Locator var locator = new Locator(); !// Inject mapService locator.setMapService(mapService); !// Use locator locator.locateUsers(users);
14
Inversion of Control
• Dependency Injection
• Service Locator
• Factory Method
IoC container:
15
Dependency Injection
• Constructor injection
• Setter injection
• Interface injection
16
Hard Coupling
17
Coupling
18
Loose Coupling
19
Плюсы• Каждый объект отвечает за свою функцию;
• Соблюден принцип инверсии зависимостей;
• Простая конфигурация объектов;
• Безболезненная смена имплементаций;
• Легко писать юнит тесты.
20
dm.js
• Javascript Реализация IoC;
• Работает в node.js и браузере;
• Легко расширяется (любые загрузчики скриптов и Promise/A+ библиотеки);
• Простая конфигурация (в духе Symfony).
21
Конфигурация "locator": { path: "path/to/locator/implementation", calls: [ ["setMapService", ["@maps"]] ] } "maps": { path: "path/to/map/service/implementation", arguments: [{ id: "my-app-id" }] }
22
Использование
dm .get("locator") .then(function(locator) { locator.locateUsers(users); });
23
Тестированиеit("should create map", function() { var mapStub, locator; ! mapMock = sinon .mock(new AbstractMapService) .expects("createMap") .once(); ! locator = new Locator(); locator.setMapService(mapMock); ! locator.locateUsers(...); ! mapMock.verify(); });
24
Syntax! "example": { "path": "...", "arguments": [{ "service": "@service", "method": "@service:method" "result": "@service:method[1,2,3]", "resource": "#path/to/my.tpl#", "path": "http://%{api_path}" "build": "build_no_#{file.txt}" }] } !
25
Альтернативы
• Wire.js
• Angular’s DI
• Тупо контейнеры
26
Где и для чего это можно использовать?
27