Javascript-форум (https://javascript.ru/forum/)
-   Angular.js (https://javascript.ru/forum/angular/)
-   -   Angular2+: Почему нельзя обойтись без dependency injection? (https://javascript.ru/forum/angular/69912-angular2-pochemu-nelzya-obojjtis-bez-dependency-injection.html)

Shitbox2 26.07.2017 15:43

Angular2+: Почему нельзя обойтись без dependency injection?
 
Стандартный пример внедрения зависимостей выглядит так:
import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Component({
  selector: 'app',
  providers: [ HttpClient ],
  templateUrl: './app.component.html'
})
export class AppComponent implements OnInit {
  constructor(
    private http: HttpClient
  ) {}

  getData(){
    return this.http.get('/url');
  }


Зачем такое усложнение, если достаточно одних ES6-модулей? Особенно это касается внедрения сервисов, которые представляют сообой, по-сути, объекты с методами или функции?
import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Component({
  selector: 'app',
  templateUrl: './app.component.html'
})
export class AppComponent implements OnInit {
  getData(){
    return HttpClient.get('/url'); //в крайнем случае new HttpClient().get('/url');
  }

destus 26.07.2017 15:48

Синглтон

Shitbox2 26.07.2017 17:07

Да знаю я всё это, читал. И ангуляровскую доку читал и статьи. Не нужно сыпать абстракциями, интересно практическое применение. Сейчас я не вижу разницы, если вместо DI использовать только es6-модули (каждый из которых синглтон), ну или делать экземпляр, если нужно. Зачем нужен DI, если стандартными средствами можно делать то же самое, только короче и проще?

Реальные примеры, пожалуйста, где одними модулями не обойтись

destus 26.07.2017 17:35

Shitbox2,
Предположим у вас будет какой-то сервис, в котором будет порядка 100 запросов и логика обработки ответов. Теперь нам нужно протестировать его. Вручную - скучно. Будем писать автоматически запускаемые юнит тесты. Так, есть. В случае, который предлагаете вы, при каждом запуске тестов будет происходить 100 запросов на сервер, а это нагрузка, это время. Хорошо получается? В случае DI вместо HttpClient я могу передать backend заглушку, которая будет отдавать заранее подготовленные данные и тестировать именно логику обработки ответов.

Shitbox2 26.07.2017 18:04

destus, ну так, тут то же самое. У нас есть
import { HttpClient } from '@angular/common/http';

Или рассмотрим для простоты commonJs модуль
const { HttpClient } = require('@angular/common/http');

Кто мешает тестовому фреймворку подменить зависимость здесь? Постоянно делаю так в node.js. Уверен и для es6 есть решения.

nerv_ 26.07.2017 18:25

Как бы вам сказать, чтобы вас не расстроить :)

На мой взгляд Angular 2+ -- это преимущественно оверхед. Разработчика обязывают писать много кода просто потому, что ребята которые проектировали ангуляр считают это лучшим выбором.

Когда еще второй ангуляр был в альфе, я понял что с ним работать не буду :D Я же не мазохист) Разве что денег много предложат.

Вообщем, "удачи вам там. Вы там держитесь" :)

destus 26.07.2017 18:34

Цитата:

Кто мешает тестовому фреймворку подменить зависимость здесь?
Пример будет? То есть у меня в коде будет написано, если запускаю на проде один импорт, если в тестах -- другой. В первом примере вы писали про "переусложнение", но реальность такова, что в случае DI нужно написать одну строчку кода (параметр конструктора). В вашем же случае, какое-то странное переопределение импортов, еще какие-то заморочки :)

Цитата:

Когда еще второй ангуляр был в альфе, я понял что с ним работать не буду
:D :D

Shitbox2 26.07.2017 22:26

nerv_,
Солидарен абсолютно. Такое же отношение и так же считаю, что они пошли куда-то не в ту сторону. Для меня они до сих пор в альфе: у них интерсепторы нормальные в http-сервисе пару недель назад появились и куча другого не доделано, что давно было в первой версии.

Тем не менее альтернативы-то нет :-( Остальные либо такие же переусложненные фреймворки, либо библиотеки типа Реакта, где кучу всего самому пилить надо (с этим вдоволь намучился). Ну vue.js... Но пока не особо радует. В Ангуляре сейчас неплохое ядро и расширяемость. Сообщество большое, не глупое. Пишутся неплохие плагины. Думаю, написать к основным сервисам адаптеры и придумать как обойти весь этот оверхед. Главное это упростить DI, чтобы им могли пользоваться js-джуниоры, а не только java-синьоры и ООП-задроты.


destus ,
Это не странное переопределение импортов, это стандартная практика. Большинство тех, кто пишет на Реакте и проч. не пользуются DI-библиотеками. В npm куча пакетов типа mock-require, которыми подменяешь зависимости. По опыту скажу, что это даже проще чем делать моки для первого Ангуляра (со вторым не пробовал еще). DI во втором Ангуляре это не одна строчка кода (дело тут совсем не в строчках), а три места, где нужно не забыть написать код, если хочешь подключить сервис: импорт, массив провайдеров и конструктор. Причем, зачем все зависимости экспортить в this класса в уме не приложу.

Так что все мои вопросы сейчас направлены на то, чтобы понять как упростить систему или увидеть преимущества существующего подхода (реальные, практические, а не соответствие каким-то абстрактным ООП канонам)

destus 27.07.2017 08:45

Цитата:

В npm куча пакетов типа mock-require, которыми подменяешь зависимости. По опыту скажу, что это даже проще чем делать моки для первого Ангуляра (со вторым не пробовал еще).
Конкретно в данной ситуации, этот вариант -- худший из возможного, потому что он требует от разработчика собственной реализации класса HttpClient (чтобы в mockExports подменять экспорт модуля @angular/common/http). А так как будет подменяться весь экспорт модуля http на наш, то нам придется реализовывать не только класс HttpClient, но и остальные экспортируемые сущности, которых очень много.

Более лучший вариант -- это использовать spy.
// наш сервис
export class Service {
    getData() {
        return new HttpClient().get('/url');
    }
    handleResponse(res: HttpResponse) { 
        // как-то обрабатываем ответ
    }
   makeRequest() {
      this.getData().map(this.handleResponse.bind(this)).subscribe()
   }
}

// тесты
it('blabla', () => {
    const service = new Service();
    spyOn(service, 'getData').and.returnValue(Observable.of(['Test1', 'Test2']))
})

Но использовать MockBackend заглушку лучше, потому что я могу протестировать своё апи с правильными параметрами, по правильному адресу и т.д. А шпионов обычно используют когда тестируют контроллер, и вместо вызова метода сервиса, возвращают заранее подготовленные данные и смотрят как эти данные, например, отрисовываются в DOM.

Цитата:

DI во втором Ангуляре это не одна строчка кода (дело тут совсем не в строчках), а три места, где нужно не забыть написать код, если хочешь подключить сервис: импорт, массив провайдеров и конструктор.
Angular-cli решает проблему. Ну то есть просто в консоли пишем ng g service Blablabla и он сам расставит нужные импорты, запишет в провайдеры и т.д.

Shitbox2 27.07.2017 10:22

Цитата:

Angular-cli решает проблему
Появление хелперов и тем более консольных утилит - первый признак несостоятельности фреймворка


Часовой пояс GMT +3, время: 03:44.