Как стать автором
Обновить

Комментарии 22

Написать кучу мягко говоря неочевидного кода вместо того, чтоб просто спокойно воспользоваться factory или factory method — это, разумеется, особый вид специальной олимпиады.

Но за старания — респект :-)

Factory Method != Inversion of Control


Поэтому нельзя "просто спокойно воспользоваться factory или factory method", чтобы получить инверсию управления. Приходится "написать кучу мягко говоря неочевидного кода". А в JS'е, с его отсутствием интерфейсов и так себе рефлексией, эта неочевидность только возрастает.


Я летом прошлого года что-то подобное DI-контейнеру пытался изобразить — teqfw/di, было интересно. Но потом фокус сдвинулся.


Добавил статью в закладки, если вернусь к теме, попробую погрузиться в эту версию мягко говоря неочевидного кода.


IoC — это массивные проекты с огромной кодовой базой и независимыми группами разрабов. В других проектах их полезность не так заметна.

Давайте я попрошу вас покликать по вашим же ссылкам, ок?
IoC реализуется в том числе и с помощью фабрики. Factory method — это частный случай фабрики.
Не всякая реализация factory method будет обеспечивать IoC, но вы можете реализовать factory или factory method так, чтоб обеспечить IoC.

Так понятнее?

"кирпич" != "дом", хотя дом может состоять из кирпичей. Так понятнее?

Вот изначальный аргумент — он как раз в том, что построить дом из кирпичей будет проще, чем реализовывать абстракцию «дом» из вороха неочевидных вещей.

Если вы считаете, что рефлексия для IoC просто нужна, и без неё совсем ой — вы строите дом из клея и песка. Даже несмотря на то, что он у вас определенно получится, если взять достаточно того и другого.

"Если вы считаете, что рефлексия для IoC просто нужна, и без неё совсем ой ..." (с)


Для DI нужна. Статья так и называется "Внедри это полностью. DI-in-JS" Попробуйте реализовать DI-контейнер без рефлексии и без декларативного описания зависимостей через конфиги. Поэтому и ворох неочевидных вещей.


Но лично вы считаете, что для JS'а хватает и фабрик.

Выскажусь в защиту мнения JustDont, потому что видел реализацию DI, например http://www.slimframework.com/docs/v4/concepts/di.html, которая представляет собой контейнер, который предлагает метод для получения зависимостей. Рефлексия — это одна частная имплементация DI.
Насколько я понимаю DI, часть приложения будет вынуждено знать о контейнере, но главное не наделять этим знанием все подряд без разбора.

По поводу очевидности частично согласен. За респект спасибо))
А вот по поводу паттерна factory непонятно. Там кода получиться не меньше, вы же должны писать кучу классов под каждый случай ConcreteCreator.create() -> ConcreteProduct. О каком DI тут речь непонятно. Либо же вы расширяете этот паттерн и в фабричный метод передаете конфиги с описанием имен и классов зависимостей.
Хотелось бы увидеть реализацию, просто я пока не понимаю что вы имели в виду.
Я в качестве IoC с недавнего времени стал использовать awilix
Затенение — плохая практика.
class Foo {
  constructor(dbConnection: DbConnection = "DbConnection")
}

т.е. ты ожидаешь объект, но инициализируешь его как строку. И делаешь это не потому что Foo это необходимо, но для того чтобы сделать его «Injectable». А Foo не должен знать что Injectable он или нет.

2 Как будет работать этот подход после того по коду пройдет `uglify`?

3. Что если мне надо внедрить не объект, а строку (или объект который является результатом выполенеия некой функции. Например DSN который будет разным для разных окружений?

Я тоже интересовался этой темой и написал свой велосипед
www.npmjs.com/package/rsdi

// your classes 
class CookieStorage {}
class AuthStorage {
  constructor(storage: CookieStorage) {}
}

// configure DI
import DIContainer, { object, get, factory, IDIContainer } from "rsdi";
 
const config = {
    "ENV": "PRODUCTION",               // define raw value
    "AuthStorage": object(AuthStorage).construct(
       get("Storage")                         // refer to another dependency       
    ),
    "Storage": object(CookieStorage),         // constructor without arguments       
    "BrowserHistory": factory(configureHistory), // factory (will be called only once)  
};
const container = new DIContainer();
container.addDefinitions(config);
 
function configureHistory(container: IDIContainer): History {
    const history = createBrowserHistory();
    const env = container.get("ENV");
    if (env === "production") {
        // do what you need
    }
    return history;
}
 
// in your code
 
const env = container.get<string>("ENV"); // PRODUCTION
const authStorage = container.get<AuthStorage>("AuthStorage");  // object of AuthStorage
const history = container.get<History>("BrowserHistory");  // History singleton will be returned
Так я вроде и написал что затенение это плохо)))
container.get — это чистой воды Service Locator, который кстати многие считают плохой практикой и анти-паттерном.
container.get — это чистой воды Service Locator,

нет. Это Dependency Injection Container. И разница есть
habr.com/ru/post/465395
Развивая тему — да, ты можешь пихать `container` во все классы и таким образом превратить его в ServiceLocator. Но мы ведь не будем так делать.

class AuthStorage {
  constructor(storage: CookieStorage) {
    this.storage = storage;
  }
}
vs
class AuthStorage {
  constructor(container: IDIContainer) {
    this.storage = container.get<AuthStorage>("AuthStorage");
  }
}
Inversion of Control – это принцип проектирования, который противопоставляется традиционному подходу тем, что если в традиционном подходе «пользовательский» код вызывает зависимости, то в случае инверсии контроля – зависимости вызывают и управляют пользовательским кодом.

То есть, проще говоря, это то что отличает фреймворк от библиотеки с набором функций.

А то что вы пытаетесь преподнести как IoC называется композиция.
Пример из https://en.wikipedia.org/wiki/Inversion_of_control

public class ServerFacade {
    public <K, V> V respondToRequest(K request, DAO dao) {
        return dao.getData(request);
    }
}


Из статьи
class Component {

    constructor(name = 'Component') {

        inject(this, name);
    }


Зависимость, управляет пользовательским кодом.

А я вот одно время кайфовал от inversify

А что изменилось?
По моим впечатлением, оно уже долгое время является самой мощной и удобной реализацией в мире JS/TS.

Перешел на другие проекты, которые написаны на других языках.

Завязка на .toString() для выполнения DI не очень хорошая мысль. Подобный подход провернули еще в первом ангуляре при иджекте сервисов по именам параметров, но это очень хорошо выстрелило в ногу с минификацией, т.к. замена имен параметров невозможна при таком подходе, а если принимать во внимание отдельный транспилятор как babel или ts, то ситуация становится еще хуже.

Также возникают вопросы с правильной типизацией всего, т.к. перебивание типа у this вызывает слишком большие вопросы как в ts, так и во flow.
но это очень хорошо выстрелило в ногу с минификацией, т.к. замена имен параметров невозможна при таком подходе

Если мне не изменяет память, то это тривиально настраивалось. У минификатора есть набор флагов. Полагаю то, что от этого подхода отказались было завязано всё таки на какие-то ещё причины.

На мой взгляд, последний — 3 вариант наиболее подходит под определения DI & IoC, тем что механизм внедрения наиболее скрыт от клиентского кода.

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

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации