На эту тему даже есть несколько презентаций, Джим Вейрих, еще кто-то -
создатель IoC Needle. Суть вкратце - они попробовали, у них получилось
хуже чем без и сделали вывод что IoC - неприменим для Ruby.
Мне кажется что то что их понимание того, что такое IoC / DI несколько
не полное (или может я что-то не так понимаю).
Восновном, все аргументы сводятся к тому что поскольку в руби и так
есть метапрограмминг то IoC не нужен. Подозреваю, что это происходит
из-за того что IoC пошли из мира Java и поскольку там нет
метапрограмминга, то байткод-манипуляция которую выполняет IoC имеет
огромное значение.
И, по моему из-за этого у многих создалось впечатление что байткод-
манипуляции и метапрограмминг - это и есть то чем должен занимаеться
IoC. Но это не так, это вообще не имеет никакого отношения к IoC, это
просто конкретная реализация этой концепции в Java.
При этом совершенно игнорируются то для чего IoC собственно и
создавался - разрешение зависимостей, автоматический поиск, загрузка,
конфигурирование и сборка.
Что самое смешное - предполагается использование IoC совместно с
'require' (никому не кажется это странным?) -особенно если учесть что
основное назначение IoC - как раз и есть - избавится от всяческих
'require' и прочих явных управлений зависимостями.
Вобшем, чуть больше ем полугода назад представился случай использовать
IoC для решения одной конкретной задачи, и по моему субьективному
мнению - использование IoC сильно ее упростило, вот этот IoC -
https://github.com/alexeypetrushin/micon
Там 2 конкретных примера - первый - краткая демонстрация что это
вообще такое и как он работает.
Первый пример - хороший пример как сделать Hello World с
использованием IoC, но при этом он также и отличный пример неверного и
неэффективного (в моем понимании) применения IoC.
Именно такое использование демонстрируется в вышеозначенных
презентациях и на его основании заключается неэффективность IoC
вообще.
Самое интересное во 2м примере, это тот-же первый пример, но
переделанный так как и должен работать (в моем понимании) IoC (он там
работает впаре с автоматическим загрузчиком классов). Там нет :require
и явных конфигов - все ищется, загружается, конфигурируется и
собирается автоматически.
Особенно заметно преимущество IoC, когда так-же автоматически, без
require, MyApp.config[:option_a] и прочего собирается приложение из
десятков компонентов разбросанных по где-то 8-11 гемам.
Вобщем, у меня ощущение - то-ли IoC незаслуженно забыты, я чего-то не
понимаю, было-бы интересно узнать что вы думаете.
P.S.
еще чуть подробнее про IoC http://ruby-lang.info/blog-ru/vy-niedootsienivaietie-moshch-ioc-u3l
а это собственно само приложение на его базе
http://ruby-lang.info/blog-ru/rad-vieb-frieimvork-po3
Когда у тебя в приложении есть четко выделяемый компонент, который
действительно изолирован от окружающих и ты можешь понять, какой ему
нужен API, это хорошо.
Можно даже надеяться, что когда-нибудь за время жизни приложения у
тебя будет больше одной реализации одного интерфейса. Ха-ха!
Вот тут мы подходим к очень болезненному моменту. Повторного
использования кода — полно. Библиотеки, которые действительно массово
используются есть на всех языках программирования. А вот обобщения API
между разными кусками кода объективно мало.
Но я считаю, что проблема спускания в библиотеку ссылки на какое-то
API — практически нерешаемая задача. Если и решаемая, то по счастливой
случайности и лишь до того момента, когда потребуется протащить
дополнительные модули, потому что потребовалась новая
функциональность.
Т.е. в реалии получается, что любой уродливый Java проект на половину
забит объявлениями интерфейсов, у которых никогда не будет больше
одной реализации. Более менее хоть что-то хоть как-то получилось с
логгерами (хотя всё равно половина проектов имеют свои логгеры с
обертками над обертками над логгерами). Как грустный пример — никаких
других примеров для аспектного программирования кроме логгеров обычно
не приводят.
Давай теперь насчёт твоего кода.
Удобно ли когда в любом месте есть logger под рукой? Это не просто
удобно, это очень важно. В ffmpeg например даже запрещено пользоваться
функциями вида puts, надо пользоваться логгером.
Удобно ли пользоваться micon.router. ? Извини, нет, особенно учитывая
что никакого другого роутера не будет. Вот не будет его и всё. А если
чем-то не устраивает текущий, то руби — не джава. Можно взять и
поправить безболезненным патчем в config/initializers и следующий
программист будет знать, куда посмотреть на предмет всяких хаков,
которые мешают обновлению рельс.
Т.е. в этом и есть проблема IoC под руби. Он создает ненужные проблемы
в и без того непростом метапрограммированном коде рельс и не решает
проблемы, которые находятся уровнем выше, чем выбор инфраструктуры
динамической конфигурации проекта.
Что до меня, то has_many :clients выглядит куда как приятнее, чем
micon.register :client, :scope => :'manager.clients' или как там это
должно быть.
16 августа 2011 г. 1:40 пользователь alexey.petrushin
<alexey.p...@gmail.com> написал:
> --
> --
> Данное сообщение отправлено Вам, так как Вы являетесь подписчиком группы "RubyOnRails to russian" на группах Google.
> FAQ группы находится по адресу: http://ru.wikibooks.org/wiki/RubyFAQ
>
> Для того, чтобы отправить сообщение в эту группу, пошлите его по адресу
> ror...@googlegroups.com
> Чтобы отменить подписку на эту группу, отправьте сообщение по адресу: ror2ru-un...@googlegroups.com
> Дополнительные варианты находятся на странице группы http://groups.google.com/group/ror2ru?hl=ru
Хм, это был как раз пример нестандартного доступа, обычно
используется
вот так:
class App
inject router: :router
def do
router.do_something
end
end
Если так тоже неудобно, то хотелось бы пример - как удобно.
> Т.е. в реалии получается, что любой уродливый Java проект на половину
> забит объявлениями интерфейсов, у которых никогда не будет больше
> одной реализации.
...
> Удобно ли пользоваться micon.router. ? Извини, нет, особенно учитывая
> что никакого другого роутера не будет. Вот не будет его и всё. А если
> чем-то не устраивает текущий, то руби -- не джава. Можно взять и
> поправить безболезненным патчем в config/initializers
1. У меня впечатление что ты концентрирушся на возможности быстрой
смены компонентов, это да, одно из преимуществ IoC, но для меня лично
это второстепенно, и не важно, как ты верно отметил - руби и так
позволяет легко это делать использую метапрограммиг и duck-typing.
Гораздо важнее другое - управление зависимостями.
Вот пример реального роутера https://github.com/alexeypetrushin/rad_core/blob/master/lib/components/router.rb
(там всего пара строчек), там видно что у роутера стоит 2 зависимости,
это позволяет легко контролировать зависимости и делать ленивую
загрузку приложения.
Можно возразить "require делает то-же самое" - нет, когда я ставлю в
зависимость компонент (у этого роутера в дереве зависимостей
есть :environment), то это означает не просто
загрузить класс Environment, а загрузить и сконфигурировать используя
конфиг продакшена.
Например компонент :models (не отдельная модель, а сам фреймворк
моделей) во время инициализации читает конфигурацию базы и выполняет
подключение.
Т.е. для того чтобы использовать компонент мне вообще абсолютно ничего
не нужно знать о нем кроме его имени.
Ни где он находится, ни из каких файлов состоит, ни как правильно и в
какой последдосательности его инициализировать, ни где его конфиги,
никак мержить конфиги по умолчанию с продакшен конфигами, ни его
зависимости (такие-же сложные компоненты).
И я могу свободно изменить любой из этих параметров, и никто из
модулей зависимых от этого компонента ничего об этом не узнает.
Т.е. получается просто отличное инкапсулирование и независимость -
просто обьяви какие компоненты тебе нужны - и они сами автоматически
появятся в нужное время.
2. Чуть подробней про конфиги, там еще есть такой файл
https://github.com/alexeypetrushin/rad_core/blob/master/lib/components/router.yml
И если мне вздумается перегрузить эти настройки - достаточно просто
поместить с таким-же названием и нужной строчкой в продакшен-пути -
они будут смержены.
Т.е. любая из настроек любого из компонентов приложения может быть
легко перегружена таким способом. И не нужно читать документацию и
помнить как именно настраивается тот или иной компонент - все они
настраиваются одинаково.
Там кстати есть еще одна проблема которую сразу не видно, и которая
собственно и вынудила меня разрезать один конфиг на множество -
компоненты инициализируются в определенном порядке, и когда весь
конфиг в одном файле - то очень сложно мержить и перегружать
настройки. А так все тривиально.
> Что до меня, то has_many :clients выглядит куда как приятнее, чем
> micon.register :client, :scope => :'manager.clients' или как там это
> должно быть.
Хм, это вообще никак не связано, совершенно из другой области.
Описание связей между моделями никак не относится к IoC.
Что именно навело на такую мысль, что-то в описаниии примера? Если
так, что именно? (я тогда поправлю описание чтобы было более понятно).
> Как говорится если у вас есть проблема и вы решили решить ее с помощью Java,
> то у вас появится еще AbstractProblem и FactoryProblem.
Я привел пример Java для того чтобы было понятно откуда на мой взгляд
идет предвзятое отношение и своеобразное восприятие IoC.
И наоборот имел ввиду что нельзя брать и использовать техники Java
напрямую, потому что они не подходят к Ruby.
Добавляется всего 3 понятия:
- register(component)
- inject(component) / micon.component
- путь к папке конфигов и компонентов 'lib/components'
- activate(scope) не в счет, он черезвычайно редко используется, и
только в очень низкоуровневой логике.
Что имеется ввиду конкретно в данном случае? Где фэктори, сложности, и
т.д.?
Кстати, забыл упомянуть - получается6 что расходов и сложностей
собственно и нет, вы почти не несете никаких дополнительных расходов
(за исключением необходимости помнить эти 3 метода и помещать
обьявления компонентов в папку lib/components)
П.С.
Хм завтра ради интереса сделаю второй пример без IoC и посчитаю число
символов.
Ок, наверно не нужно было использовать термин IoC, он лишком много
всего обозначает.
Правильней будет назвать это Dependency Injection, или другими
словами: Micon - это компонент который автоматически разбирает
зависимости, собирает и конфигурирует приложение.
Или, более подробно, он позволяет сделать следущее:
- разделить приложение на части которые ничего не знают друг о друге
кроме имени и нескольких публичных методов
- полностью избавится от конфигов в коде
- просто перегружать любые значения конфигурации любых компонентов в
различных вариантах сборки и енвайронментах
- полностью избавится от 'require' и вообще какого-либо упоминания
зависимостей в виде путей и файлов
- автоматически управлять жизненным циклом группы связанных
компонентов (напр создавать и удалять request/params/session/
controller/view на каждый запрос)
- автоматически собирать компоненты для тестов и обеспечить изоляцию
между тестами (восстановение состояния после теста)
- за все это практически не нужно ничем платить (кроме запоминания 3х
методов и папки lib/components)
П.С.
Кстати, спасибо за отзывы, уже понял что то описание которое есть -
неверное: под IoC каждый понимает что-то свое и во вторых - IoC
отвечает на впорос "как делает", а место этого нужно дать ответ на
вопросы "зачем нужен/что делает".
Как можно автоматически сделать то, над чем приходится серьезно
подумать человеку?
>
> - разделить приложение на части которые ничего не знают друг о друге
> кроме имени и нескольких публичных методов
Не будет такого в реальной жизни. Зависимости проникают между всеми
слоями приложения.
> - полностью избавится от конфигов в коде
Зачем?
> - просто перегружать любые значения конфигурации любых компонентов в
> различных вариантах сборки и енвайронментах
Зачем?
> - полностью избавится от 'require' и вообще какого-либо упоминания
> зависимостей в виде путей и файлов
Зачем?
> - автоматически управлять жизненным циклом группы связанных
> компонентов (напр создавать и удалять request/params/session/
> controller/view на каждый запрос)
Уже сделано:
block do
Request.new
end
> - автоматически собирать компоненты для тестов и обеспечить изоляцию
> между тестами (восстановение состояния после теста)
Уже сделано. См test/unit или rspec
> - за все это практически не нужно ничем платить (кроме запоминания 3х
> методов и папки lib/components)
и более непонятного связывания компонент.
Откуда такая уверенность, что динамическое связывание компонент — это хорошо?
Ведь ребенку понятно, что код вида object->setWidth() более понятен,
чем object->invoke("setWidth", ...), потому как
это постоянно заканчивается object->invoke(method, args) и становится
совершенно непонятно, куда дальше
уйдет управление.
> > полностью избавится от конфигов в коде
> Зачем?
> > - полностью избавится от 'require' и вообще какого-либо упоминания
> > зависимостей в виде путей и файлов
> Зачем?
Зачем делают рефакторинг и удаление ненужного и лишнего кода? Чтобы
упростить систему.
> > просто перегружать любые значения конфигурации любых компонентов в различных вариантах сборки и енвайронментах
> Зачем?
чтобы приложение можно было строить в виде многократно используемых
модулей, которые просто настраивать в различном окружении: blog/forum/
cms
а не в виде одной папки app/controllers/* где находится много-много
десятков контроллеров.
> > - разделить приложение на части которые ничего не знают друг о друге
> > кроме имени и нескольких публичных методов
> Не будет такого в реальной жизни. Зависимости проникают между всеми
> слоями приложения.
Скажем так, этого почти не бывает в рельсах, точнее я не видел, (хотя
в последних версиях с енджинами вроде что-то сделать можно, кстати
нисколько их не критикую за это и не считаю это их минусом, они
оптимизированы для другого), но это не значит что такого не бывает
вообще. Фактически вы утверждаете что создание слабо-связанных систем
в реальной жизни не возможно, я с этим не согласен - не все можно
изолировать, но некоторые вещи можно.
> > - автоматически собирать компоненты для тестов и обеспечить изоляцию
> > между тестами (восстановение состояния после теста)
> Уже сделано. См test/unit или rspec
Нет, не сделано, если мне нужно протестировать только роутер или
контроллеры в отдельности от всего остального - rspec за меня не
выдернет их из приложения и не сделает так чтобы они заработали без
всего остального.
И если я в процессе тестов нарушу в роутере и контроллере всю
конфигурацию и умолчания - он опять-же ничего за меня не восстановит
как было до теста.
> и более непонятного связывания компонент.
наоборот, когда все модули системы инициилизируются по одному и тому-
же сценарию - все становится проще, знаешь куда идти и где искать, чем
когда каждый модуль имеет свой собственную уникальную инициализацию.
> Откуда такая уверенность, что динамическое связывание компонент -- это хорошо?
> Ведь ребенку понятно, что код вида object->setWidth() более понятен,
> чем object->invoke("setWidth", ...), потому как
> это постоянно заканчивается object->invoke(method, args) и становится
> совершенно непонятно, куда дальше
> уйдет управление.
это как посмотреть, когда этот object->invoke('setWidth', ...) всего в
одном экземпляре, а конструкций типа object->setXxx() сотни
разбросанных по всей системе - то что это спорный вопрос что проще.
П.С.
кстати, если вам что-то показалось непонятным или странным в
документации - дайте знать пожалуйста,
ActiveRecord делает тривиальными тривиальные запросы, которые в силу
говорливого SQL-я выглядят достаточно громоздко, хотя от этого не
перестают быть тривиальными.
Если запрос на SQL действительно сложный, то ActiveRecord не спасает.
Причём тут IoC я не понимаю.
> Так-же и тут, большая часть всех зависимостей сводится к таким вещам
> как: загрузить зависимости, загрузить код, загрузить конфиг, собрать и
> связать с другим приложением. Micon разрешает такие случаи
> автоматически.
Вот в чём проблема то в рельсах загрузить код? Даже require писать уже
не надо: автолоадер стал достаточно предсказуемым и очень удобным.
Загрузить конфиг проще простого: Config = YAML.load(Rails.root +
"/config/app-" + Rails.env + ".yml")
Зачем мне тут дополнительный уровень косвенности?
> Зачем делают рефакторинг и удаление ненужного и лишнего кода? Чтобы
> упростить систему.
>
Покажи код, который ты считаешь сложным и как ты его упростишь? Я не
увидел пока этого в примерах.
> Скажем так, этого почти не бывает в рельсах, точнее я не видел,
Во-первых «я не видел» — это хорошая поправка. На деле возникают проблемы.
Вот делали мы биллинг. Думаешь это очень просто так задизайнить
систему приёма платежей так, что бы её можно было выдрать из одного
приложения и вставить в другое? Хрен там. Я вообще не уверен в том,
что это возможно сделать и причина тому в том, что действительно
разнообразная бизнес-логика делает механизм обобщенного наследования
кода неприемлемо сложным.
Т.е. условного говоря написать обобщенный фреймворк на все случаи
жизни становится хуже, чем копипастить какие-то куски каждый раз.
> наоборот, когда все модули системы инициилизируются по одному и тому-
> же сценарию - все становится проще, знаешь куда идти и где искать, чем
> когда каждый модуль имеет свой собственную уникальную инициализацию.
Куда идти то? Написан какой-то абстрактный вызов какого-то модуля,
который неизвестно где определится.
Куда мне смотреть следующей строчкой?
> это как посмотреть, когда этот object->invoke('setWidth', ...) всего в
> одном экземпляре, а конструкций типа object->setXxx() сотни
> разбросанных по всей системе - то что это спорный вопрос что проще.
Явные вызовы легче отследить.
>
> П.С.
> кстати, если вам что-то показалось непонятным или странным в
> документации - дайте знать пожалуйста,
>
Непонятно, какая проблема решается.
> Покажи код, который ты считаешь сложным и как ты его упростишь? Я не
> увидел пока этого в примерах.
Хм, там вроде есть (во вчерашних примерах не было, но седня в новом я
добавил это), в случае без Micon'а нужен такой код:
# reading application config
require 'yaml'
Ultima.config = YAML.load_file "#{dir}/config/config.yml"
# initializing router
require 'router'
router = Router.new
router.url_root = Ultima.config['url_root']
Ultima.router = router
# loading request and controller
require 'request'
require 'controller'
# Assemblilng Application
require 'pages_controller'
А во втором примере, с Micon - все это делается автоматически.
По настоящему сложный пример привести сложно потому что это-же всетаки
пример а не полноценное приложение.
> Непонятно, какая проблема решается.
Я лично использую его в качестве склета для небольшлго фреймворка -
клона рельсов ( http://ruby-lang.info как пример простого сайта).
Одной из задач было - сделать возможным разделение как приложения так
и самого фреймворка на части, и пытаясь сделать это я заметил что
многократно повторяю один и тот-же сценарий связанный со сборкой
компонентов. Я выделил его в отдельный фреймворк - это собственно
Micon и есть.
Но наверно да, такой низкоуровневый компонент, без конкретного примера
- не особо понятно зачем он нужен.
On 16 авг, 23:51, Max Lapshin <max.laps...@gmail.com> wrote:
> 2011/8/16 alexey.petrushin <alexey.petrus...@gmail.com>:
>
> > Так-же как AcriveRecord делает тривиальными вполне себе сложные
> > запросы. Он делает простыми запросы на которые приходится 90% всех
> > случаев использования и прямой доступ к драйверу если нужно что-то
> > специфичное.
>
> ActiveRecord делает тривиальными тривиальные запросы, которые в силу
> говорливого SQL-я выглядят достаточно громоздко, хотя от этого не
> перестают быть тривиальными.
>
> Если запрос на SQL действительно сложный, то ActiveRecord не спасает.
> Причём тут IoC я не понимаю.
>
> > Так-же и тут, большая часть всех зависимостей сводится к таким вещам
> > как: загрузить зависимости, загрузить код, загрузить конфиг, собрать и
> > связать с другим приложением. Micon разрешает такие случаи
> > автоматически.
>
> Вот в чём проблема то в рельсах загрузить код? Даже require писать уже
> не надо: автолоадер стал достаточно предсказуемым и очень удобным.
> Загрузить конфиг проще простого: Config = YAML.load(Rails.root +
> "/config/app-" + Rails.env + ".yml")
> Зачем мне тут дополнительный уровень косвенности?
>
> > Зачем делают рефакторинг и удаление ненужного и лишнего кода? Чтобы
> > упростить систему.
>
> Покажи код, который ты считаешь сложным и как ты его упростишь? Я не
> увидел пока этого в примерах.
>
> > Скажем так, этого почти не бывает в рельсах, точнее я не видел,
>
> Во-первых <<я не видел>> -- это хорошая поправка. На деле возникают проблемы.
On Aug 17, 3:53 am, Oleh Khomey <varyf...@gmail.com> wrote:
> Regards,
> Oleh Khomey
>
> 2011/8/16 alexey.petrushin <alexey.petrus...@gmail.com>