Форум Flasher.ru
Ближайшие курсы в Школе RealTime
Список интенсивных курсов: [см.]  
  
Специальные предложения: [см.]  
  
 
Регистрация Блоги Правила Справка Пользователи Календарь Поиск рулит! Сообщения за день Все разделы прочитаны
 

Вернуться   Форум Flasher.ru > Блоги > Good

Оценить эту запись

Инверсия контроля

Запись от artcraft размещена 14.04.2012 в 03:12
Обновил(-а) artcraft 12.09.2012 в 19:31

Инверсия контроля (Inversion of Control, IoC) - это важный принцип ООП

Подождите, какой контроль? Контроль чего нужно инвертировать?
Имеется в виду контроль над созданием зависимостей.

Представим себе автомобиль,
без мотора он не сможет работать, потому нужен мотор.
Код AS3:
public class Car{ 
    private var engine:IEngine = new Engine(); 
}
С первого взгляда выглядит как превосходный код. Но принцип инверсии тут не соблюдается.
Дело в том, что автомобиль должен ездить и перевозить грузы, а не создавать моторы и другие запчасти.
Создавая себе мотор, автомобиль занимается тем, для чего он не предназначен, и становится фабрикой на колёсах.

Название: blog_attachment.jpg
Просмотров: 1144

Размер: 13.3 Кб

Легко себе представить, во что превратится код этого класса, когда окажется, что автомобиль не может хорошо работать без колёс, руля, магнитолы с DVD, и большого количеств других важных частей.

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

Есть и вторая проблема с этим автомобилем.
Если вдруг потом понадобится заменить паровой двигатель на бензиновый, то придётся разбирать машину (рефакторить) и налаживать в ней производство нового типа двигателя, и так с каждой маркой машин.
А если двигатели будет производить только моторный завод, то производство новых моторов достаточно будет наладить только один раз.

Теперь, поняв в чём вред отсутствия инверсии контроля,
можно перечислить выгоды от её наличия:
  1. позволяет модулю сфокусироваться на своей задаче
    (не быть фабрикой на колёсах)
  2. ослабляется связанность классов между собой
    (мотор больше не неотъемлемая часть автомобиля)
  3. обеспечивает простоту замены модулей
    (вместо одного мотора всегда можно подсунуть другой мотор)
  4. упрощает тестирование и ремонт
    (вместо мотора всегда можно подсунуть пустышку)

Реализовать этот принцип можно разными способами
  • Внедрение зависимости (Dependency injection)
    • Через конструктор (Constructor injection)
    • Через сеттер (Setter injection)
    • Через интерфейс (Interface injection)
  • Патерн фабрика (Factory pattern)
  • Service locator (Service locator pattern)
  • IoC контейнер(Ioc-container)

Тут уже гугль вам в помощь.


Приведу только пример с использованием SwiftSuspenders
(который используется в RobotLegs)

Код AS3:
======= IEngine.as ========
public interface IEngine {}
 
======= Engine.as ========
public class Engine implements IEngine {
	public function Engine(){
		trace('engine constructed');
	}
}
 
======= Car.as ========
public class Car {
 
	[Inject]
	public var engine:IEngine;
 
	public function start():void{
		if(engine) trace('it works');
	}
}
 
======= Main.as ========
import flash.display.Sprite;
import org.swiftsuspenders.Injector;
 
public class Main extends Sprite{
	public function Main(){
		var car:Car = new Car();
		var injector:Injector = new Injector();
		injector.map(IEngine).toType(Engine);
		injector.injectInto(car);  // engine constructed
		car.start();  //it works
	}
}

Полезная ссылка:
перевод статьи Мартина Фаулера "Inversion of Control Containers and the Dependency Injection pattern"
Всего комментариев 42

Комментарии

Старый 14.04.2012 12:39 f.g.programmer вне форума
f.g.programmer
 
Аватар для f.g.programmer
Наверно в [Inject] и наборе инструкций инициализирующих двигатель есть какой-то смысл. Но я не вижу чем
Код AS3:
var injector:Injector = new Injector();
injector.map(IEngine).toType(Engine);
injector.injectInto(car);
лучше
Код AS3:
car.engine = new Engine();
Если предлагать что-то, что работает лучше, то было бы не плохо объяснить почему.
Старый 14.04.2012 13:37 in4core вне форума
in4core
 
Аватар для in4core
Цитата:
лучше
Код AS3:

car.engine = new Engine();
Ясное дело так лучше, так и понятнее
Старый 14.04.2012 13:40 artcraft вне форума
artcraft
 
Аватар для artcraft
в этой конкретной ситуации ничем, но в большом проекте выгода будет явной

1. конфигурация отделена от производства
всегда можно сделать так
Код AS3:
injector.map(IEngine).toType(BetterEngine);
и это повлияет на все девайсы использующие моторы

2. куча интересных опций вроде asSingleton, toValue, toProvider, soft, shared, seal, [Inject(optional)], [PreDestroy]
Код AS3:
injector.map(IEngine).toType(Engine).asSingleton();
Старый 14.04.2012 14:09 etc вне форума
etc
 
Аватар для etc
Проблема только в том, что если машина подписана на события старого мотора, а ей его кто-то поменял, то получится треш.
Старый 14.04.2012 14:18 artcraft вне форума
artcraft
 
Аватар для artcraft
Треш в такой ситуации получится вне зависимости от того каким образом при этом был создан мотор.

SwiftSuspenders для разрешения такой ситуации диспатчит InjectionEvent-ы PRE_CONSTRUCT и POST_CONSTRUCT
и еще есть метатег [PostConstruct]

a еще проще сделать так:
Код AS3:
[Inject]
public function set engine(engine:IEngine):void{
	// first unplug old engine
	_engine = engine;
	// now plug in new engine
}
Обновил(-а) artcraft 14.04.2012 в 15:29
Старый 14.04.2012 16:33 etc вне форума
etc
 
Аватар для etc
Меня ещё смущает отсутствие инкапсуляции в таком инжекторе. Надо хотя бы неймпейс свой иметь для таких вещей.
Старый 14.04.2012 16:52 fljot вне форума
fljot
Что-то пункт "Через интерфейс (Interface injection)" как-то выбивается из списка. Т.е. первые два про то, как ссылку передаём, а этот совсем про другое.
Старый 14.04.2012 17:10 artcraft вне форума
artcraft
 
Аватар для artcraft
@etc я не предлагаю всем брать и пользоваться именно SwiftSuspenders, он тут как пример IoC контейнера

@fljot вот тут пример (правда не AS3)
Старый 14.04.2012 17:44 TanaTiX вне форума
TanaTiX
 
Аватар для TanaTiX
Мне по ходу работы приходится использовать RL. Выгода одна (я другой не вижу) - этот фрймворк предлагает структуру проекта. Т.е. если своя страдает или отсутствует - юзайте, выгода будет однозначно.
Старый 14.04.2012 18:39 wvxvw вне форума
wvxvw
 
Аватар для wvxvw
До примера было все замечательно - а с конкретным примером есть все те же проблемы, что и с любыми мета данными и рефлексией:
- не возможно нормально в отладчике посмотреть.
- куча лишнего кода (зависимостей).
- неявные манипуляции затрудняют понимание происходящего.
Конкретные проблема этого пример (да и вообще "инжекта"):
- он создается асинхронно с инициализацией класса (позже), изза чего приходится еще писать кучу кода, который в другой ситуации был бы не нужен.
- использование интерфейса ничем не обосновано в данном случае и только увеличивает количество погонных метров кода на решение задачи.

Т.о. мое мнение - обычный человеческий сеттер делает то что нужно не в пример лучше.
Старый 14.04.2012 19:59 artcraft вне форума
artcraft
 
Аватар для artcraft
Повторюсь, я не евангелист Роботлегса или SwiftSuspenders, и нигде ни одного слова не написал о том что это лучший способ реализовать инверсию контроля или что другие способы хуже.

Про метаданные целиком согласен - затрудняют понимание того откуда растут ноги.
Никакая сверх-мега-крутая библиотека не переплюнет сеттер в простоте, плюшками запросто, а вот лаконичностью никак.
Старый 14.04.2012 22:46 Rzer вне форума
Rzer
 
Аватар для Rzer
Из википедии:
Цитата:
APIs that use inversion of control
SAX is an example of an API that uses inversion of control throughout (after initialisation). SAX is generally more efficient than DOM, but DOM is often considered more convenient to program with, because it is not necessary to deal with inversion of control.[7]
Есть ли реальный пример зачем это нужно? и где удобней использовать?
Старый 14.04.2012 23:34 MikroAcse вне форума
MikroAcse
 
Аватар для MikroAcse
Боже мой, мне так стыдно, что я не знаю, как использовать эти ваши Синглтоны, Интерфейсы...
Но почему-то мне это кажется бесполезным.
Старый 14.04.2012 23:40 Aquahawk вне форума
Aquahawk
 
Аватар для Aquahawk
MikroAcse Как и классы, как таковые, многим кажутся бесполезными по началу. Всё это нужно когда проекты становятся действительно большими, а программистов много.
Старый 15.04.2012 00:01 MikroAcse вне форума
MikroAcse
 
Аватар для MikroAcse
Aquahawk А пример их использования можешь дать? Как они помогают?
Старый 15.04.2012 00:48 in4core вне форума
in4core
 
Аватар для in4core
MikroAcse что за наезды? Почитайте в гугле зачем нужны интерфейсы, это старинная тема. Насчет синглтонов там же. Сам паттерн синглтон, по сравнению с интерфейсами конечно используется в разы реже, и собственно без него жить можно, но иногда нужен. Статей куча - чтите.
Старый 15.04.2012 00:58 Aquahawk вне форума
Aquahawk
 
Аватар для Aquahawk
MikroAcse Любой проект размером более 10 тыс строк кода. Когда чего-то становится много, это что-то надо как-то организовать. Все эти штуки придуманы чтоб организовать сложные структуры. Пока структуры маленькие и организовавыть нечего.
Старый 15.04.2012 02:22 artcraft вне форума
artcraft
 
Аватар для artcraft
Добавил в статью ссылку на авторитетный источник.

@Rzer

Про то как устроен джавовский SAX знаю только понаслышке, поэтому ничего сказать об этом не могу. Под инверсией контроля иногда имеют в виду ещё и немного другое понятие, вот статья про инверсию другого контроля. Возможно в этой цитате речь именно об этой другой инверсии.

Цитата:
Есть ли реальный пример зачем это нужно? и где удобней использовать?
Зачем нужно я уже написал, и про вред от отсутствия и про пользу от соблюдения.

В тех ситуациях когда есть пачка взаимозаменяемых плагинов, из которых одновременно используется только один, необходимость такого подхода очевидна. Во всех остальных случаях, если выносить создание зависимости за пределы класса, то вреда не будет, и главное, когда вдруг окажется, что нужно расширить в том месте где изначально этого не планировалось, шанс нарваться на трудности будет гораздо ниже


Цитата:
Боже мой, мне так стыдно, что я не знаю, как использовать эти ваши Синглтоны, Интерфейсы...
Но почему-то мне это кажется бесполезным.
кажется
Обновил(-а) artcraft 15.04.2012 в 02:27
Старый 15.04.2012 03:58 in4core вне форума
in4core
 
Аватар для in4core
Цитата:
MikroAcse Любой проект размером более 10 тыс строк кода.
Не соглашусь.
300-500 строк кода в файле уже бывает часто достаточно для разграничения. Примером может служить вид ( мвс ) , в котором 20 методов вызываемых контроллером, и 40 приватных методов, мешанных между собой. Вот тут интерфейс как нельзя кстати, мы сразу сможем увидеть какие же методы принимает наш вид, не ковыряясь в коде глубоко. Про кастование здесь смысла говорить нет, оно и не нужно по сути, здесь интерфейс как раз как информативный класс. Хотя... каждому свое
Старый 15.04.2012 19:22 alatar вне форума
alatar
 
Аватар для alatar
Цитата:
- не возможно нормально в отладчике посмотреть.
Что невозможно в отладчике посмотреть?
Старый 15.04.2012 23:22 Psycho Tiger вне форума
Psycho Tiger
 
Аватар для Psycho Tiger
Цитата:
Вот тут интерфейс как нельзя кстати, мы сразу сможем увидеть какие же методы принимает наш вид, не ковыряясь в коде глубоко.
Для этого существуют хорошие IDE с автокомплитом и аутлайнами. Самыми популярными с этими возможностями для AS3 являются FD, FDT, IDEA, FB. Ознакомтесь, и Ваша разработка станет гораздо проще.
Старый 16.04.2012 00:50 wvxvw вне форума
wvxvw
 
Аватар для wvxvw
Цитата:
Что невозможно в отладчике посмотреть?
Поставить бряк на метадату?
Старый 16.04.2012 11:55 alatar вне форума
alatar
 
Аватар для alatar
Цитата:
Поставить бряк на метадату?
Если метадата относится к сеттеру, то брекпоинт можно поставить в нем. На публичную переменную тоже нельзя бряк поставить.
Старый 16.04.2012 12:44 wvxvw вне форума
wvxvw
 
Аватар для wvxvw
Поэтому публичные (а так же другие типы переменных) плохо поддаются отладке.
От того, что я поставлю бряк на сеттере ничего не изменится. Я буду тестировать сеттер а не код, который его вызывает. Может код по какой-то причине его не вызывает (например не совпадают типы), а я об этом даже не знаю.
Старый 16.04.2012 14:11 artcraft вне форума
artcraft
 
Аватар для artcraft
На метадату и не надо ставить брэйкпоинтов.
Метатэг это метка для инжектора, сам он ничего не делает.

По сути строчка injector.injectInto(car); обозначает - собрать машину (пройтись по всем помеченным сеттерам и передать им то что указано в конфиге). Это можно сделать и руками, но тогда нужно будет написать не одну строчку а по строчке на каждый сеттер.

Поставив брэйк на сеттер будет видно откуда он вызван, а если брэйкпоинт не остановит приложение, то это тоже результат.

И еще, SwiftSuspenders при инжекте выбросит ошибку если в конфиге нету нужной информации, или не совпадают типы
Обновил(-а) artcraft 16.04.2012 в 14:21
Старый 16.04.2012 14:29 wvxvw вне форума
wvxvw
 
Аватар для wvxvw
Мы говорим об абсолютно разных вещах. Предположим, у нас есть метадата:
[Foo(boo)]
а используем мы ее так, что нам на самом деле нужно doo вместо boo. Например:

Код AS3:
[Inject(com.boo.Injected)]
var injected:Injected;
и вот представьте, что у вас есть только дебаггер - сколько дней у вас займет понять в чем именно заключалась ошибка. (Предположим, класс com.doo.Injected тоже существует).
Старый 16.04.2012 14:54 alatar вне форума
alatar
 
Аватар для alatar
Цитата:
Может код по какой-то причине его не вызывает (например не совпадают типы), а я об этом даже не знаю.
Эти причины легко выявляются. Ничуть не сложнее, чем при собственной системе доставки.
Цитата:
Мы говорим об абсолютно разных вещах.
А зачем предполагать заведомо корявый вариант?
Цитата:
и вот представьте, что у вас есть только дебаггер - сколько дней у вас займет понять в чем именно заключалась ошибка.
Даже при таком раскладе ошибка будет найдена сразу, в том месте где этот класс используется.
Старый 16.04.2012 15:02 artcraft вне форума
artcraft
 
Аватар для artcraft
если мы говорим о конкретно этой бибилоитеке, то в метатэге не указывается что именно нужно инжектировать, метатэг это указатель на то что в этот сеттер нужно произвести нижект извне согласно конфигу (тоже внешнему)


вот скрин брэйкпоинта
прекресно видно откуда вызван сеттер
Старый 16.04.2012 15:24 wvxvw вне форума
wvxvw
 
Аватар для wvxvw
Нет, ну что вы, что вы, я вас уверяю, все сделать легко, имея необходимые знания и инструменты! Проблема в том, что дебаггер не поможет это выяснить по причине, которую я объяснил выше (т.е. не является подходящим инструментом), и ошибка эта не очевидная - т.е. нет необходимых знаний. А так, да, конечно все будет в лучшем виде, в сжатые сроки, всенепременно.

Еще раз: сеттер не вызвался - и вы не знаете почему, а не вызвался и вы смогли это зафиксировать. Это 99% всей головной боли от [Bindable] когда убиваешь по нескольку часов на то, чтобы выяснить почему до конкретного места выполнение не дошло. А вы рассказываете про какие-то тривиальные ситуации, которые в жизни вообще не встречаются, или на столько тривиальны, что никто бы и не подумал, что они могут представить сколь-нибудь существенные сложности.

Вариант с метаданными является заведомо корявым в 100% случаев. Мы как раз их и обсуждаем. Какие еще не корявые варианты вы имеете в виду?
Обновил(-а) wvxvw 16.04.2012 в 15:29
Старый 16.04.2012 15:40 artcraft вне форума
artcraft
 
Аватар для artcraft
Если сеттер не вызывался а над ним стоит метатег [Inject], то значит после создания этого объекта в него не впрыснули зависимости на этапе конструирования - 2 клика и ошибка найдена.

И кстати, если подобная ошибка произойдёт в проекте без метатегов, то искать ошибку будет только труднее. (не так очевидно в какой момент и откуда планировалось этот сеттер вызывать)
Обновил(-а) artcraft 16.04.2012 в 15:58
Старый 16.04.2012 16:55 wvxvw вне форума
wvxvw
 
Аватар для wvxvw
Вот именно, что вы не правы ни в первом, ни во втором. Даже в АС3, даже в Флеш Билдере есть такая штука как "найти все использования" - если вы напрямую вызываете сеттер - найти место откуда вы его вызываете - тривиальная задача. Найти место откуда он вызывается используя метдату не тривиально, а, иногда и невозможно (если используется библиотека без исходников).

Почему не появился "инжекнутый" объект - тому может быть очень много причин. Ошибка в конструкторе "инжекнутого" объекта, ошибка в коде фреймвокра который его инжектит, опечатка в имени "инжектнутого" объекта - ничего из этого прямо на ошибку не укажет, потому что скорее всего рантайм ошибки ловятся идиотскими фреймворками, опечатки в метадате никак не проверятются компилятором.
Старый 16.04.2012 17:24 artcraft вне форума
artcraft
 
Аватар для artcraft
"найти все использования" - никогда не полагался на такую функцию, именно по той причине что она находит не всё

вот если вместо [Inject] написать [Injcet] или [inject], a ещё лучше [lnject] то действительно можно долго тупить и искать в чём дело
но остальное ситуации, лично мне, кажутся достаточно прозрачными
Обновил(-а) artcraft 16.04.2012 в 17:40
Старый 16.04.2012 17:48 artcraft вне форума
artcraft
 
Аватар для artcraft
кстати IDEA на незнакомый метатег выдаёт weak warning, так что, если не средствами компилятора, так средствами IDE и этой ошибки можно избежать
Старый 17.04.2012 18:21 incvizitor вне форума
incvizitor
 
Аватар для incvizitor
Вообще иногда бывают свободные минуты времени, которые я уделяю рассуждению о надобности ДИ. На работе сейчас юзаем swiz, и скажу что вообщем опыт весьма позитивный. Кстати такой очепятки там не получится.

Код AS3:
[Inject]
public var bugaga:IFoo;
Сам найдет единственную реализацию IFoo, которая лежит в маппинге свиза. Бывают конечно стремнаватые ситуации когда реализация IFoo не одна и нужно по айдишнику вылавливать ту, которая нам нужна. Но такие ситуации возникают крайне редко, во всем проекте сейчас может 3 - 4 таких места, и они потихоньку истребляются.

В итоге в проекте сейчас около 70 модулей, и все они зависят лишь от фреймворковых, либо базовых модулей проекта. Так что пока что всё довольно хорошо. Сама теория ДИ, меня смущает и лично у меня есть на этот счет некоторые опасения. Но на практите, вроде как, работает стабильно.
Обновил(-а) incvizitor 18.04.2012 в 16:27
Старый 09.05.2012 23:21 expl вне форума
expl
2 incvizitor:
Конкретная ситуация (описал только один аспект):

- Eсть контроллер over 2000 строк, который знает о куче объектов
- Контроллер пичкает разными объектами вручную через конструкторы команды, отправляющие запрос на сервер и меняющие соотв. образом модель.
- разные окошки берут комманды так: controller.newAddItemToInventory(item).run();
- инвентарь, юзера(для изменения денег), сам класс для отправки запросов, и кучу другого контроллер подставляет сам.
- в итоге куча передач параметров и здоровенный интерфейс (здоровенный, потому что в нём описаны все методы для всех комманд)

Решил создавать комманды инжектором там где они нужны (injector.resolve(MyCommand) as MyCommand), и все бы ничего, но:

- текущий пользователь меняется когда заходишь к другу
Это что получается, нужно менять меппинг у инжектора при переходе?
- некторые комманды при создании в контроллере слушаются им и производятся некоторые действия по окончанию
Замепил в инжекторе на метод, который это делает и возвращает комманду, но чем это удобнее, если бы я просто оставил публичный метод newCommand()?

Вобщем, очень пугает "нединамичность" этой штуки - замепил, собралось и всё
Неужели таких проблем не возникало?

И 2-е:

Говорят, что правильное использование контейнера означает вызов только одного resolve в точке входа - а остальное - без контейнера происходит заинжектенными в приложение фабриками.

Но тогда непонятно зачем он вообще нужен - ведь можно с тем же успехом вместо конфигурации вручную с помощью new понасоздавать все фабрики и объекты в одном стартовом методе - ну кода только больше будет, зато всё прозрачно и зависимостей внуть полезет не больше чем при использовании контейнера с одним вызовом resolve.
Не понимаю короче.

Иногда кажется, что рефлексия хороша только для запуска тестов
Обновил(-а) expl 09.05.2012 в 23:35
Старый 12.05.2012 03:57 incvizitor вне форума
incvizitor
 
Аватар для incvizitor
2 expl,

Ну 2000 строк это точно не есть хорошо. Я делаю так (когда не юзаю ДИ, а простое МВЦ): Моделька кидает всплывающие событие. В нутри события лежит комманда, внутри комманды лежит респондер. Контроллер ловит евент у основной модельки, скармливает комманду сервер коммуникатору. сервер коммуникатор создает объект реквеста и передает ему ссылку на респондер. когда приходит ответ с сервака, дергается респондер который сам изменит в модели все что нужно. То есть контроллер о типах запросов особо то и знать ничего не должен. То есть функционал контроллера в коде выглядит так:

Код AS3:
dataBase.addEventListener(CommandEvent.COMMAND, _onCommandRecived);
private function _onCommandRecived(e:CommandEvent):void{
      dispatchCommand(e.command);//диспатч комманды словит серер коммуникатор.
}
Но вернемся к ДИ:

Цитата:
- текущий пользователь меняется когда заходишь к другу
Это что получается, нужно менять меппинг у инжектора при переходе?
Зачем? У тебя есть въюшка допустим фермы. ты перешел на ферму друга и скормил въюхе другую модель. Просто в менеджере (который будет осуществлять переход) нужно заинджектать репозиторий юзеров (из которого можно получить юзера по идишнику), модельку которая хранит текущей фермы. В коде это так:

Код AS3:
 
//Менеджер переключения фермы
 
public class FarmManager{
      [Inject] //таким образом инджектается объект
      public var friendsRespository:IFriendsRepository;
 
      [Inject]
      public var farmModel:FarmModel;
 
      [EventHandler(event="ChangeFarmEvent.CHANGE_FARM", properties="userID")]
      //Вот так в свизе можно ловить евенты которые бросает медиатор
      public function onUserChanged(userID:String):void{
            farmModel.currentUser = friendsRespository.getUserByID(userID);
      }
}
 
//Базоваю въюшка фермы
 
public class FarmViewBase extends Sprite{
      [Inject]
      [Bindable]
      public var farmModel:FarmModel;
}
 
//Въюшка фермы
 
<view:FarmViewBase>
      <someNS:Image uri="{farmModel.currentUser.iconURI}"/>
</view:FarmViewBase>
Цитата:
И 2-е:
Начиная с этой строчки ничего не понял
Обновил(-а) incvizitor 12.05.2012 в 14:30
Старый 14.05.2012 01:54 expl вне форума
expl
О, спасибо за ответ!
(сам этот задержал, т.к. времени нехватало)

--------------------------------------------------------
Сначала по поводу 2-го вопроса:
Коли речь в посте о SwiftSuspenders,
то там центральная роль отводится Injector-у, на него мепятся зависимости и правила создания объектов:
Код AS3:
instance.map(IMyClass).toType(MyClass);
// MyClass принимает в конструкторе типы IA и IB
instance.map(IA).toType(A);
instance.map(IB).toSingleton(B);
// Дальше настраиваем зависимости для A и B и т.д.
..
// Собираем зависимости
var instance:IMyClass = injector.getInstance(IMyClass);
Так вот, возникает соблазн поюзать injector не только на старте приложения,
но и внутри этих классов MyClass, A и т.д. Дабы не протягивать туда параметры через конструкторы и всяческие сеттеры

Да и вообще если надо фабрику протянуть - её же делать классом придётся,
не заинжектишь же IFactory, про который в рефлексии не написано, что он производит
Городить метаданные?
Код AS3:
[Inject(factoryOf="ru.xxx.xxx.IMyClass")]
public var myFactory:IFactory;
Увольте!
А так - вместо factory.newInstance() - injector.getInstance(MyClass).

Чем чревато тянуть инжектор - я понимаю. Я не понимаю зачем он нужен, если его не тянуть.
Ну в самом деле, неужели, если не тянуть инжектор нельзя настроить приложение ручками:
Код AS3:
// MyClass принимает в конструкторе типы IA и IB
var c:C = new C();
var a:IA = new A(c);
var d:D = ...
...
var b = new B(a, c, d);
var instance:IMyClass = new MyClass(a, b);
Ну кода больше получится, и то не факт. Ну, может ещё переписывать больше при изменении параметров класса, но платить за это выявлением ошибок с "непередачей параметра в конструктор" только во время выполнения?
------------------------------
Однако, я не нашел в swiz никакого getInstance или resolve.
Тоесть что получается ральный подход, все объекты берутся не у _какого-то объекта по типу_, а через специальные методы "как-бы фабрик"?
Т.е. глядя на класс снаружи всегда можно сказать объекты каких интерфейсов он использует?

-----------------------------
Цитата:
Но вернемся к ДИ:

Цитата:
- текущий пользователь меняется когда заходишь к другу
Это что получается, нужно менять меппинг у инжектора при переходе?
Зачем? У тебя есть въюшка допустим фермы. ты перешел на ферму друга и скормил въюхе другую модель. Просто в менеджере (который будет осуществлять переход) нужно заинджектать репозиторий юзеров (из которого можно получить юзера по идишнику), модельку которая хранит текущей фермы. В коде это так
Ага, делаем типизированный провайдер, который и передаем везде вместо инжектирования данных напрямую.
Т.е. получается, что инжекция используется только для неменяющихся данных... (ну или контейнеров меняющихся). В чём же ее прикол, в удобной настройке приложения для тестов и для боя?

----------------------------
Цитата:
Ну 2000 строк это точно не есть хорошо. Я делаю так (когда не юзаю ДИ, а простое МВЦ): Моделька кидает всплывающие событие. В нутри события лежит комманда, внутри комманды лежит респондер. Контроллер ловит евент у основной модельки, скармливает комманду сервер коммуникатору. сервер коммуникатор создает объект реквеста и передает ему ссылку на респондер. когда приходит ответ с сервака, дергается респондер который сам изменит в модели все что нужно. То есть контроллер о типах запросов особо то и знать ничего не должен. То есть функционал контроллера в коде выглядит так:
Использовать модель как шину событий контроллера Хотя контраргумент только один (может оно в большинстве случаев и зашибись): иногда в зависимости от одних и тех же действий одной и той же вьюшки(или разных, но одного и того же класса) главный контроллер принимает разные меры. Это возможно когда события от вьюшки слушаются разными подконтроллерами, а не центральным у модели.

Про респондер, он у тебя ответ с сервера разбирает и меняет модель?
А комманда - это объект с необходимой для запроса информацией?
(Если это так, то у меня комманда - это и данные для сервера и разбор ответа с изменением модели, она, правда, сама сервер-коммуникатор дергает)
Про нелёгкий путь события с командой и респондером и в итоге передачу управления последнему - понятно.

За кадром остался вопрос: Кто создаёт событие? Этот товарищ должен обладать информацией, нужной респондеру и комманде для формирования запроса на сервер.

Вот эти данные и не хотелось тянуть в окошки, поэтому глобально меняющиеся и постоянные осели в контроллере и сделали его интерфейс непомерно большим (хотя их доля в 2000 строках не велика - там другого хватает). Этот интерфейс как-то даже стабами и моками для тестов подменять рука не подымается.

Но, кажется родилось другое решение:
- берём и подписываем к методам, возвращающим комманды (в моём понимании этого слова) вот такой тег:
Код AS3:
[Provider]
public function newAddToInventoryCommand():AddToInventoryCommand
- регим весь Controller в injector'е как провайдер
- берём комманды во вьюшках как и хотели в прошлый раз:
Код AS3:
AddToInventoryCommand(injector.get(AddToInventoryCommand)).addCallbacks(...).run(itemThatMastAdded);
Всё! Раньше мне не нравилось, что мепить неудобно - приходится отдельные методы указывать и еще параметры неявно для комманд мепить, а они меняются. Теперь всё протягивается вручную - а интерфейс сузился до одного метода! (хоть и не по феншую - см. "Сначала по поводу 2-го вопроса")
Обновил(-а) expl 14.05.2012 в 03:01
Старый 14.05.2012 03:15 incvizitor вне форума
incvizitor
 
Аватар для incvizitor
expl, я говорил конкретной реализации ДИ (той что во свизе). Можно инжектать и по интерфейсу, если для данного интерфейса существует только одна имплементация. То есть фреймворк понимает что тебе именно нужно скормить. Если имплементаций данного интерфейса несколько, то пойдут конфликты.

Я не знаком с SwiftSuspenders, так что не смогу ничего о нем расказать.

Цитата:
Т.е. получается, что инжекция используется только для неменяющихся данных...
Считайте что можно получить ссылку на любой промапленный объект в почти любом классе приложения. Это могут быть и провайдеры, и модельки, и сервер коммуникатор и все что угодно. Выгода в том, что объект не надо протягивать, он передается через ДИ.

Цитата:
Т.е. глядя на класс снаружи всегда можно сказать объекты каких интерфейсов он использует?
Да, сокрытие информации порядочно хромает.
Старый 14.05.2012 03:40 expl вне форума
expl
Цитата:
expl, я говорил конкретной реализации ДИ (той что во свизе). Можно инжектать и по интерфейсу, если для данного интерфейса существует только одна имплементация. То есть фреймворк понимает что тебе именно нужно скормить. Если имплементаций данного интерфейса несколько, то пойдут конфликты.
Вот надо в объект A загнать фабрику объекта IB
Если сделать класс BFactory - то всё понятно, он заинжектится
Если не делать класса, а использовать стандартную фабрику, например ClassFactory - то как, кроме метаданных, сказать какой тип объекта мы хотим создавать? (из генериков то у нас только Vector, но даже если его тип в xml-описание попадает, фабрику из него не сделаешь)
Т.е. не тип фабрики - нас он не волнует, а тип создаваемого объекта? Ну если только с помощью IFactory во всём проекте мы только один тип объекта создаём - тогда никаких метаданных не потребуется.

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

- в конструкторе - который впринципе в интерфейс не идет

- в некоторых сеттерах, через которые инжектит инжектор, но в интерфейсе и без них неплохо - они нужны только для внутренней работы класса.

Т.е. простой интерфейс не составляет труда подменить Стабом/моком, когда тестируем зависимые от интерфейса классы.

Но когда тестируем сам класс - нам совершенно очевидно от чего он зависит.
Совсем наоборот - когда класс внутри что-то берёт через injector.get(Type) - поди догадайся какой тип в инжекторе ему надо замепить.

Цитата:
Считайте что можно получить ссылку на любой промапленный объект в почти любом классе приложения. Это могут быть и провайдеры, и модельки, и сервер коммуникатор и все что угодно.
Выгода в том, что объект не надо протягивать, он передается через ДИ.
Но "дай мне сюда объект этого типа" вы нигде в коде не пишите, насколько я понял? Всё инжектит фреймворк по меппингу?

[бред]
...можно сконфигурировать вручную, но слепить вручную фабрику, которую не надо переписывать при изменении параметров... может IoC-контейнер не просто удобный конфигуратор, ан-нет - фабрики то хреново инжектятся - т.е. в них вручную надо встраивать injector...
[/бред]
Обновил(-а) expl 14.05.2012 в 03:54
Старый 16.05.2012 01:05 incvizitor вне форума
incvizitor
 
Аватар для incvizitor
Цитата:
Всё инжектит фреймворк по меппингу?
В меппиниге описаны только классы которые можно инжектать это выглядит так:

Код AS3:
<?xml version="1.0" encoding="utf-8"?>
<swiz:BeanProvider xmlns:swiz_="http://swiz.swizframework.org"
				   xmlns:swiz="org.swizframework.core.*"
				   xmlns:mx="http://www.adobe.com/2006/mxml"
				   xmlns:flasher="http://www.flasher.ru"
		>
 
	<flasher:SomeManager/>
        <swiz:Prototype type="{Model}" singelton="true"/>
 
	<mx:Script><![CDATA[
		]]></mx:Script>
</swiz:BeanProvider>
Вот этот MXML код промэпливает все 2 класса SomeManager и Model. Первый сразу создается сразу и может хендлить события которые проходят через медиатор, второй создастся только когда кто то его попросит. Всё, эти классы может просить любой зарегестрированный в ДИ контейнере объект.

"дай мне сюда объект этого типа" пишиться тегом [Inject]. Ваще это можно в доке свиза почитать, она за 30 мин спокойно читается.

Фабрики хреново инжектятся, по крайней мере я не нашел хорошего способа их инжектать. Но скажу по правде, лично мне пришлось инжектать фабрику только 1 раз
Обновил(-а) incvizitor 16.05.2012 в 01:21
Старый 16.05.2012 11:58 expl вне форума
expl
Попытался я почитать эту доку, похоже действительно всё инжектирование выполняется внутри фреймворка, а не в пользователских классах.
Обилие метаданных, конечно, пугает, особенно с событиями. Ну да ладно.
Старый 24.05.2012 00:28 djyamato вне форума
djyamato
 
Аватар для djyamato
Если я правильно понял, то инъекции можно создавать где угодно
 

 


Часовой пояс GMT +4, время: 16:54.


Copyright © 1999-2008 Flasher.ru. All rights reserved.
Работает на vBulletin®. Copyright ©2000 - 2019, Jelsoft Enterprises Ltd. Перевод: zCarot
Администрация сайта не несёт ответственности за любую предоставленную посетителями информацию. Подробнее см. Правила.