Инверсия управления | |
---|---|
S-type Автор Сообщений: 2969 Дата регистрации: 24.04.2004 |
Что такое DI - понятно. А вот при чём тут IoC - так и не пойму. В википедии [url]https://ru.wikipedia.org/wiki/Инверсия_управления[/url] какое то мутное определение "IoC - это принцип". Но, в чём именно заключается этот принцип - не поясняется. В книжке "Spring в действии" (Крейг Уоллс, третье издание) термин "IoC" упомянут два раза - и оба за пределами авторского текста. Т.е. Крейг Уоллс термин IoC не употреблял, полагаю - намеренно.
Так в чём именно заключается инверсия? Исправлено 1 раз(а). Последнее : S-type, 08.03.18 11:37 |
Re: Инверсия управления | |
---|---|
Igor Korolyov Сообщений: 34580 Дата регистрации: 28.05.2002 |
martinfowler.com IoC это общий принцип - фреймворк крутит основной цикл приложения, вызывая когда надо "прикладной" код. Как для получения визуальной разметки - скажем какие пункты вставить в главное меню, какие текстбоксы вывести в панель при активации модуля "ввод нового клиента", так и для реакции на пользовательские действия (обычная "подписка на события" это тоже вариант IoC). Без IoC собственно прикладной код "выводит меню, текстбоксы, опрашивает клавиатуру, ловит нажатия мышкой" - вызывая для этого функционал фреймворка. Собственно вот это и есть "инверсия". DI это просто один из шаблонов реализующих принцип IoC - он конкретно определяет как прикладной компонент получает ссылки на нужные ему другие компоненты (как прикладные, так и системные). По крайней мере я так это понимаю. ------------------ WBR, Igor |
Re: Инверсия управления | |
---|---|
S-type Автор Сообщений: 2969 Дата регистрации: 24.04.2004 |
это один конкретный пример. А как "в общем"? Какой то корявый вопрос, попробую переформулировать. Что такое зависимость? Объект класса B зависит от объекта класса A, если объект класса B содержит ссылку на объект класса A. Выходит, зависимость - это ссылка на другой объект. Внедрение зависимости (DI) - это получение ссылки на другой объект откуда то извне (не важно откуда). Когда объект класса B получает ссылку на объект класса А (при этом объект класс А уже создан), и не важно как получает (через конструктор или сеттер) - это и есть "внедрение зависимости". Противоположность внедрению зависимости (отсутствие DI) - когда создание объекта типа А происходит внутри объекта типа В. Судя по всему, в случае DI инверсия заключается в том, что объект типа А создаётся не в объекте типа B, а создаётся где то "во вне" - за пределами объекта типа B. Т.е. объект типа B не причастен к созданию объекта типа A / объект B не контролирует создание объекта А / объект B не управляет созданием объекта типа A. По поводу "во вне". Это может быть как простой код (назовём это ручным внедрение зависимости), так и контекстом Spring (автоматическим внедрением зависимости). DI - подмножество IoC, относящееся к зависимостям. IoC - это общий принцип. И этот принцип заключается в том, что _______ - вот тут не могу подобрать слов. К чему ещё применяется IoC, кроме зависимостей? |
Re: Инверсия управления | |
---|---|
Igor Korolyov Сообщений: 34580 Дата регистрации: 28.05.2002 |
Это как раз "в общем" - фрейморв вызывает прикладной код, а не прикладной код вызывает фреймворк. Т.е. если очень упрощённо, то main это часть фреймворка при инверсии. Тут больше педалируется тот факт что А ЗНАЕТ все подробности про В - собственно говоря потому он и может его создать. В случает DI А не знает ничего про В кроме интерфейса (который, интерфейс этот, описан вообще ВНЕ В). Соответственно реализация В может быть какой угодно (основной рабочий код, специальный какой-то, или вообще тупой mock для тестирования) - и А на это никак не может повлиять. Т.е. речь идёт про "зацепление" aka coupling модулей/классов. Да, в коде фреймворка. Если это прикладной код, то это уже не DI... К общему "потоку управления" программы. Если его контролирует фреймворк, вызывая по необходимости прикладные модули - это IoC. Если его контролирует прикладной код, вызывая для своих нужд функции фреймворка - это "прямой контроль". В той или иной степени IoC есть практически везде - даже в фоксе, его READ EVENTS и декларативно описанная перед ним система меню - это IoC. Вот FPD-стиль с выводом группы PROMPT-ов, организацией цикла ожидания выбора и потом вызовом нужных процедур в соответствии с выбором - это "прямое управление". ------------------ WBR, Igor |
Re: Инверсия управления | |
---|---|
S-type Автор Сообщений: 2969 Дата регистрации: 24.04.2004 |
Почему? Какая разница - кто и где создал другой объект? Зависимость - это ссылка. Внедрение зависимости - получение ссылки извне. А кто создал объект (на который получена ссылка) - разве это существенно? Разве не может быть DI без IoC? |
Re: Инверсия управления | |
---|---|
Igor Korolyov Сообщений: 34580 Дата регистрации: 28.05.2002 |
Так по теории правильно будет - DI реализуется в IoC контейнере, а не просто прикладной код создающий объекты и связывающий их друг с другом... Вероятно в этом случае таковой код уже не будет "прикладным" - ну, точнее, не ДОЛЖЕН быть прикладным... Обязанность создавать объекты и пропихивать их каким-то потребителям весьма далека от прикладных задач "посчитать скидку" или там "напечатать отчёт об остатках". И по SOLID никак не получится такой класс - контейнер внедрения зависимостей - иметь в прикладном слое приложения.
Я так понимаю что все реализации DI (лично я только с умершим CAB-ом имел дело) основаны на контролируемом создании объектов - т.е. уже изначально речь идёт по какой-то сервис умеющий создавать разнообразные (произвольные) объекты, и пропихивать в них опять же разнообразные (произвольные) ссылки на другие объекты. Назвать такой сервис прикладным как-то язык не поворачивается А если он абсолютно не универсальный, т.е. грубо говоря умеет только ссылку на Logger прописать в свойство Logger трёх заранее известных создаваемых им же классов... Вряд ли это можно назвать полноценным DI Это какая-то специфическая "фабрика", не более того. ------------------ WBR, Igor |
Re: Инверсия управления | |
---|---|
S-type Автор Сообщений: 2969 Дата регистрации: 24.04.2004 |
В теории от Мартина Фаулера? Да, в ссылке на статью, что ты привёл:
[attachment 29110 injector.gif] Но, как уже не раз говорил: моё мнение, Фаулер - это балабол, пишущий слишком мутно и с кучей ошибок. Ему нет доверия. Мне больше нравится такое мнение:
Т.е. DI - это паттерн. И, ни какого отношения к тому, как создаются объекты, передаваемые по ссылке, данный паттерн не имеет. Можно написать код, применив паттерн DI. При этом внедрение зависимостей будет "ручным". А можно написать код с помощью фреймворка Spring - тогда внедрение зависимостей можно сделать автоматическим. Разве не так? Т.е. ты считаешь, что не может быть DI без IoC. Может, поменяешь мнение? IMHO, проставлять ссылки в объекты - да, это реализация DI. Но, кто мешает всё делать вручную? Ведь, по сути, Spring - это код, кем то написанный. Если я напишу код, внедряющий зависимости, разве можно будет сказать - что я не использую DI? Если я не оформлю код, внедряющий зависимости, в виде отдельной библиотеки, разве можно будет сказать - что я не использую DI? Вопрос о полноценности - это ведь уже другой вопрос. Тут факт - реализовали DI или нет, вот в чём вопрос. А то, что он реализован только "в узком формате" - это второе. |
Re: Инверсия управления | |
---|---|
S-type Автор Сообщений: 2969 Дата регистрации: 24.04.2004 |
Что то мы всё словами, без конкретного кода. Предположим, есть два класса:
Применим паттерн DI (конкретно, паттерн для внедрения через конструктор). Получим:
Класс B принимает в конструкторе ссылку на объект класса A, и эту ссылку сохраняет. Если без Spring-а, то придётся писать:
Ещё вопрос - а в какой именно момент происходит "внедрение зависимости"? В [1], [2] или [3]? Или, внедрение зависимостей это [1]+[2], или [1]+[3], или только [3] или ещё какая то комбинация? А вот внедрение зависимости на Spring фреймворке (это работающий код):
Автоматическое - потому, что нет кода для получения ссылки на объекта класса A! Непосвящённым кажется, будто тут есть определённая магия. На самом деле магии нет - это связано с тем, что так работает Spring. Если в контексте приложения имеется только один бин типа (класса) A, тогда любой бин, имеющий свойство типа A (в данном случае это параметр конструктора B), будет зависеть именно от этого бина. Зависимость сильная, потому что создана без использования интерфейса. Объект b, созданный с помощью фреймвока Spring хоть и называется (в терминах Spring фреймворка) бином, и объект b, созданный без Spring фреймворка (командой new) - это абсолютно одинаковые объекты, это самые обыкновенные объекты Java (POJO). Между ними нет ни какой разницы. Фреймворк получает управление потоком в моменты вызова AnnotationConfigApplicationContext (создаёт бины) и getBean (возвращает ссылку на нужный бин). Убедил? Исправлено 1 раз(а). Последнее : S-type, 10.03.18 12:58 |
Re: Инверсия управления | |
---|---|
Igor Korolyov Сообщений: 34580 Дата регистрации: 28.05.2002 |
Тут нет противоречий с Фаулером, хотя это и несколько более широкая трактовка DI. В теории не имеет (т.к. это просто констатация отсутствия зависимости класса от другого класса), на практике - для C# имеет. Распространённых IoC контейнеров есть с десяток, не считая самописок. Они все следуют примерно одному и тому же шаблону - и о "ручном коде" ни в одном из них речи не идёт. Т.е. ни один из них реально не знает для каких классов и какие именно зависимости будут внедряться. Если ты сумеешь меня в этом убедить Тут ведь вся фишка как раз в том что внедрение зависимости это и ЕСТЬ IoC, одно из проявлений принципа. Т.е. ты пытаешься изобрести штыковую лопату, которая при этом НЕ будет лопатой Суть не в том что это отдельная библиотека, и не в том что она написана каким-то Чандрамарапаном и выложена в nuget репозиторий, а не тобой и имеется только в одном твоём проекте (хотя изобретение велосипедов - порочная практика. Лучше уж допилить существующий, чем свой с нуля делать). Суть в том что это специализированный модуль (обычно это несколько десятков классов) который реализует абстракцию шаблона DI. Т.е. "в итоге" мы получаем при создании объекта класса А наличие в нём ссылки (формально не обязательно "ссылки" - зависимость может быть и не ссылкой на другой объект, но для простоты ограничимся объектами) на некоторый объект реализующий определённый контракт. Основной плюс будет если класс А зависит от контракта (интерфейса) B, но не зависит от собственно реализующего контракт класса. Потому в общем то и не поощряется внедрение зависимостей с конкретными классами, а не интерфейсами (не все реализации даже позволяют прописывать класс, а не интерфейс для "внедряемой" ссылки). ------------------ WBR, Igor |
Re: Инверсия управления | |
---|---|
S-type Автор Сообщений: 2969 Дата регистрации: 24.04.2004 |
Если считать, что Фаулер дал частный "пример", то противоречия нет. А если дал исчерпывающее и законченное определение - то противоречие есть, т.к. в общем случае наличие объекта "Assembler" не обязательно. DI - это не констатация отсутствия зависимости. DI - это способ управления зависимостями. И, что подразумевается под "имеет отношение"? В Spring + Java бин - это самый обыкновенный POJO. И, нет разницы - внедрена зависимость вручную или через Spring. В С# разве будет иначе? Если контейнер действительно НИЧЕГО не знает, то он ни когда ни чего внедрить не сможет. В любом случае контейнер что то знает, и заполнение поля объекта ссылкой на другой объект (т.е. внедрение зависимости) ВСЕГДА будет сопровождаться наличием каких то правил (т.е. знаний, кодом) - в примере выше показал это. То, что контейнер внедряет что то не явно, а с помощью определённых правил (как говорят некоторые "магией Spring") - это всего лишь способ автоматизации сопоставления, но это ни как не способ реализации DI-паттерна. Хорошо. Если есть DI, значит уже есть IoC. Но, если есть IoC, то не обязательно это DI. Так? Но, тогда у меня "рвётся шаблон" в другом месте. Ты говорил, что IoC - это когда фреймворк крутит основной цикл приложения. Но, в том же Spring-фреймворке это не так. Есть контейнер с объектами. Фреймворк создаёт объекты, внедряет в них ссылки, выдаёт объекты "по требованию" в основную программу. При этом фреймворк ни как не "крутит основной цикл приложения". Выходит, Spring - это не Ioc-контейнер? Почему именно "специализированный модуль"? В чём именно заключена "абстракцию шаблона DI"? Как уже говори, на мой взгляд принцип DI - это то, что "объект, на который внедряется ссылка, создаётся где то вовне". Всё. Это и есть "инверсия управления"! Получая ссылку "из вне" объект не контролирует его создание, по сути - объект "передаёт полномочие на создание объекта" кому то другому. И, не важно, кто этот другой - фреймворк или моя программа. Под "контролирует его создание" подразумевается создание объекта и заполнение его данными (например, через конструктор или сеттеры). Опять таки, не важно - кто создаёт сам объект, в который внедряется ссылка. Исправлено 3 раз(а). Последнее : S-type, 10.03.18 19:16 |
Re: Инверсия управления | |
---|---|
S-type Автор Сообщений: 2969 Дата регистрации: 24.04.2004 |
Если так подумать, то в последнем абзаце я ответил на вопрос "что такое инверсия управления" в случае DI.
Пошёл как я в тернажёрный зал... |
Re: Инверсия управления | |
---|---|
Igor Korolyov Сообщений: 34580 Дата регистрации: 28.05.2002 |
Обязательно наличие чего-то, что будет внедрять зависимости (два POCO объекта не могут этого сделать сами по себе). Почему бы эту штуку не называть Assembler? Хотя сейчас более привычно называть это IoC-контейнер. DI это одна из реализаций принципа IoC, которая в основном служит для устранения "зацепления" классов. "Имеет отношение" - значит что НЕЛЬЗЯ воспользоваться старым добрым var a = new A(); и получить внедрение зависимости в этот самый A (ну на самом деле, конечно, можно - только это будет уже ни разу не классический DI, а какой-то иной шаблон). И да, в шарпе это тоже "обычный POCO" - хотя для некоторых реализаций контейнеров и "декорированный" специальными атрибутами (скажем параметры конструктора, или свойства для хранения ссылок помечаются атрибутами, по которым IoC контейнер понимает что сюда надо прописать ссылку). "Ручной код" - это половина пути к DI - он не универсален, не гибок, многословен, и потому практически не применим. Как только код становится более-менее универсальным, не зависящим от конкретики связываемых объектов, он тут же превращается в реализацию DI Отчего же? В "тупом" варианте он вполне себе может и абсолютно ничего не знать о связываемых POCO объектах. Через рефлексию получить все описанные в сборке (или всех загруженных сборках) типы (словарик "что мы умеем внедрять"), для запрашиваемого типа просмотреть все его свойства (к примеру), или все параметры конструктора (это ещё более тупой вариант, хотя тогда параметризованный конструктор должен быть всего один - иначе "внедрятель" не будет знать какой именно использовать), и создать экземпляр, попутно создав по экземпляру на каждый найденный "требуемый тип" (а т.к. он "тупой", то всё то же самое должен будет проделать и для создаваемых "зависимых" объектов - главное тут не попасть в цикл, или уметь обходить его - скажем подсовывая ссылку на УЖЕ созданный экземпляр). Как правило это так - контейнер тем или иным способом узнаёт о "правилах" ("тупой" контейнер как описано выше практически бесполезен - с ним придётся лишь бороться, а не облегчать себе жизнь). Может явно через код типа "для свойств типа IOne подставлять экземпляр типа OneImplementation", может опосредованно - "для свойств помеченных атрибутом [ServiceDependency(typeof(IOne))] подставлять экземпляр типа помеченного атрибутом [Service(typeof(IOne))]". Конечно же "работать" эту кухня будет только если экземпляр запрошен у контейнера, а не создан прямым вызовом new A(); Не обязательно. Скажем для той же самой цели устранения "зацепления" классов может применяться шаблон ServiceLocator. Впрочем даже и безо всяких "устранений зависимостей" можно использовать другие IoC моменты - т.к. это очень широкий подход. И это ТОЖЕ. IoC это принцип "переворачивания контроля", он может применяться в самых разных местах ПО для самых разных целей. IoC-контейнер - только один из аспектов IoC как принципа. Нет не всё. А собственно "внедрение ссылки"? То что объект создан снаружи - пол дела. Он и для обычного синглтона "создан снаружи", и для "фабрики" тоже, только там не идёт речи о DI. Не только создание, но и получение ссылки! Именно потому и создаваться объект должен не напрямую, а через механизмы соответствующего IoC-контейнера, иначе каким волшебным образом ссылка попадёт к этому самому объекту? Почему ты считаешь что "твоя программа" это 100% прикладной код? Если там есть модуль создающий объекты и прописывающий в них ссылки - т.е. выполняющий функции IoC-контейнера, то это как раз и есть СИСТЕМНЫЙ код - хотя и написан он тобой. И да, ВАЖНО кто создаёт объект. Нет возможности передать ссылки в конструктор объекта если не этот же самый код (который должен прописать ссылки) и создаёт этот объект! Даже для внедрения через свойства это важно, т.к. ссылки должны быть заполнены тотчас же после создания объекта - а не "когда-то там потом другим кодом". У внедрения через конструктор есть одно важное преимущество - можно пользоваться "внедрёнными сервисами" уже во время инициализации объекта - все прочие способы не позволяют этого добиться. Но они должны обеспечить такое поведение, что в "прикладном" коде, вызвавшем var a = IoCContainer.Get<A>(); уже сразу же в следующей строке кода можно без ошибок воспользоваться a.SomeMethodUsingInjectedService(); Т.е. все зависимости уже внедрены. И внедрены они именно где-то внутри IoCContainer.Get<Type>() - а это как раз и есть "системный" код, даже если этот класс написал ты сам (хотя смысла в этом лично я не вижу). Код вида
Убери 2 первые строки в специальный класс - и получишь DI, хоть и дубовый, "ручной" ------------------ WBR, Igor |
Re: Инверсия управления | |
---|---|
S-type Автор Сообщений: 2969 Дата регистрации: 24.04.2004 |
Вот тут не согласен категорически. Данный пример (весь, целиком) - это и есть самое непосредственное "внедрение зависимости", зависимость внедряется через свойство (сеттер). "Зависимость" - это "ссылка", "внедрение" - это "получение извне". Вывод - внедрение зависимости, это получение ссылки на объект откуда то извне. И, кто создал объект, на который получена ссылка - к DI отношения не имеет. Начал гуглить и нашёл habrahabr.ru , конкретно - абзац "Объяснение внедрения зависимостей". Исправлено 1 раз(а). Последнее : S-type, 11.03.18 19:39 |
Re: Инверсия управления | |
---|---|
Igor Korolyov Сообщений: 34580 Дата регистрации: 28.05.2002 |
Ну так почитай дальше, там всё разжёвано, почему ПЛОХО писать код типа
P.S. И про "зависимости" в начале цикла внимательно почитай - а то ты, видимо, полагаешь что написав
------------------ WBR, Igor Исправлено 1 раз(а). Последнее : Igor Korolyov, 11.03.18 20:13 |
Re: Инверсия управления | |
---|---|
S-type Автор Сообщений: 2969 Дата регистрации: 24.04.2004 |
Вопрос не в качестве кода. Вопрос в том, что в этом коде есть внедрение зависимости. И, для DI наличие отдельного специального класса "Assembler" совсем не обязательно. Убедил? Зачем нужны контейнеры - это уже следующий вопрос. Как уже писал, с помощью контейнера можно автоматизировать сопоставление параметров классов и их зависимостей (и даже пример на Java+Spring привёл). Но, прежде чем за Spring браться, надо же понять - что такое DI Ну, зачем, дорогой, наговариваешь? Я же выше по русски написал:
|
Re: Инверсия управления | |
---|---|
Igor Korolyov Сообщений: 34580 Дата регистрации: 28.05.2002 |
Нет не убедил. Для меня DI всегда неразрывно связан с тем или иным IoC контейнером внедряющим зависимости и, соответственно, конструирующим объекты. Без него я лично никакого DI не вижу (так же как и ООП в VBA, например - хотя там и "свойства" и "объекты" имеют место быть). По крайней мере в шарпе. Т.е. по моему мнению в приведенных примерах только явовский вариант имеет DI.
И, насколько я понимаю, не я один такой ------------------ WBR, Igor |
Re: Инверсия управления | |
---|---|
S-type Автор Сообщений: 2969 Дата регистрации: 24.04.2004 |
Т,е. твоё мнение, что для реализации DI-паттерна наличие специального класса "Assembler" (упомянутого в статье Мартина Фаулера) обязательно. У меня мнение - специальный класс "Assembler" для реализации паттерна DI не требуется.
Моё определение "внедрение" - это "получение извне". Твоё определение термина "внедрение" - это "получение от контейнера DI". И, как можно видеть, можно найти ссылки подтверждающие как твоё мнение, так и моё. Думаю, пора ставить точку. |
Re: Инверсия управления | |
---|---|
Igor Korolyov Сообщений: 34580 Дата регистрации: 28.05.2002 |
Так уже ж
У меня нет большого опыта в DI - кроме CAB-овского ObjectBuilder-а вкупе с другими MS patterns & practices наворотами (а там их ну очень много - их фреймворк это нечто - при том что по сути он никогда и не был доведен до стадии "зрелого продукта" - хотя из него "выросли" Unity и Prism - жаль последний это WPF) я ничего реально не использовал. Но оно померло уж давно (а проект с их использованием нет ) потому совсем недавно я и озаботился поиском/выбором более современных IoC средств (для начала хотя бы эту часть, уж про UI с каким MVP подходом да REST сервисы со стыковкой для десктопа пока совсем рано думать). И то что я видел - ну "как под копирку" Потому я и говорю что это не только моё личное мнение... Хотя, конечно же, в первую очередь личное. P.S. Если забыть про "теорию", то что будет "на практике" применимо к C#? Unity, Ninject, Castle Windsor, StructureMap, Spring.NET, Autofac... Достаточно бегло взглянуть на них, чтобы понять общий принцип. Ну и цитата из www.dotnettricks.com Цитата: ------------------ WBR, Igor Исправлено 1 раз(а). Последнее : Igor Korolyov, 12.03.18 00:33 |
Re: Инверсия управления | |
---|---|
S-type Автор Сообщений: 2969 Дата регистрации: 24.04.2004 |
Мне не довелось дожить до DI в C#. Java всё больше проникает в мои мозги, которые сопротивляюсь этому отсталому языку. Шучу. Не критично, но хватает некоторых мелочей (например, out-параметров). После JavaFX (идеология чем то похожа на VPF) изучаю ServiceMix, а это Karaf. Как написано на karaf.apache.org Programming Model (Spring / BluePrint / DeclarativeService). Вот, и полез копать Spring.
Конечно, ситуация не такая критичная, как с зоопарком MV*, но всё равно - много противоречий, что бы всё "уложилось" в голову. В той же "Spring в действии" Крейга Уоллса на странице 36 он пишет: Цитата: Что может "координировать работку каждого объекта в системе"? Понятно, что это может быть "контейнер". Но, [почему?] не сказано же явно, что "контейнер", а как то аморфно "третья сторона". От и гадай - что это. А мнения, увы, разделились. Исправлено 1 раз(а). Последнее : S-type, 12.03.18 01:18 |
Re: Инверсия управления | |
---|---|
S-type Автор Сообщений: 2969 Дата регистрации: 24.04.2004 |
Что в переводе: Цитата: Т.е. проводится разница между "управлением зависимости" и "внедрением зависимости". Исправлено 1 раз(а). Последнее : S-type, 12.03.18 09:43 |
© 2000-2024 Fox Club  |