Comments 22
Но за старания — респект :-)
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 тут речь непонятно. Либо же вы расширяете этот паттерн и в фабричный метод передаете конфиги с описанием имен и классов зависимостей.
Хотелось бы увидеть реализацию, просто я пока не понимаю что вы имели в виду.
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
class AuthStorage {
constructor(storage: CookieStorage) {
this.storage = storage;
}
}
vs
class AuthStorage {
constructor(container: IDIContainer) {
this.storage = container.get<AuthStorage>("AuthStorage");
}
}
То есть, проще говоря, это то что отличает фреймворк от библиотеки с набором функций.
А то что вы пытаетесь преподнести как IoC называется композиция.
public class ServerFacade {
public <K, V> V respondToRequest(K request, DAO dao) {
return dao.getData(request);
}
}
Из статьи
class Component {
constructor(name = 'Component') {
inject(this, name);
}
Зависимость, управляет пользовательским кодом.
Также возникают вопросы с правильной типизацией всего, т.к. перебивание типа у this вызывает слишком большие вопросы как в ts, так и во flow.
На мой взгляд, последний — 3 вариант наиболее подходит под определения DI & IoC, тем что механизм внедрения наиболее скрыт от клиентского кода.
Имхо, наследование — наименее "скрытый" механизм. По сути весь проект вынужден наследоваться от одного базового класса, который не имеет отношения к предметной области. И да, не забываем явно вызывать родительский конструктор из потомков.
del
Внедри это полностью. DI-in-JS