Здравствуйте, Tom, Вы писали:
Tom>·>Service Locator. Tom>Так не надо его использовать. Контейнер то тут причём
Ещё как причём. Открываешь первую попавшуюся доку по контейнерам и там этим всё пестрит, как будто так и надо.
И ты вот уже неосознанно используешь "константу как при регистарции так и при резолве". Регистация и резолв это и есть SL. Т.е. ты регистируешь в реестре инстанс по ключу "тип" или "тип+простая_строка", потом его достаёшь:
container.Register<IFoo, Foo>();
var instance = container.GetInstance<IFoo>();
Для DI не нужны никакие регистрации и резолвы.
Tom>Конечно есть некоторые исключения — вершина стека вызовов. Аля новый поток но тут ничего не сделать.
Не нужны никакие исключения.
public static void main(args)
{
var app = new AppModule();
app.run(args);
}
Tom>Мне казалось я понятно обьяснил зачем нужны строки.
Ты объяснил для чего ты их используешь, но не объяснил зачем ты используешь именно строки, а не миллион других способов выразить полиморфизм.
Tom>1-е для того что бы сказать контейнеру что он не должен переопределять зависимость а должен зарегистрировать вторую для того жде интерфейса.
Зачем зарегистрировать-то?? С какой целью?
Tom>2-й для того что бы как то можно было зарезолвить зависимость по имени. Таких случаев тьма, случаи когда есть N обьектов реализующих один и тот же интерфейс, как пример ICommand. И никаких проблем строки не создают, обьявите их константами и используйте.
Почему именно по строковому имени? А если у меня long-идентификатор? или тупо bool? Потому что контейнер по-другому не умеет. Да ведь?
Если тебе так надо, то так и напиши: Map<String, IFoo>, или switch-case, или if. Вон сколько есть выразительных средств, а ты вынужден использовать только строки, т.к. даже самый современный контейнер по-другому особо и не умеет.
Tom>>>Ничего не понимаю что куда залетело. В общем случае фича которая называется auto wire работает прекрасно. Регистрируешь только то что отличается от принятых в контейнере конвенций. Tom>·>Я о http://www.lightinject.net/#assembly-scanning , а ты о чём? Tom>И я о том же, обычно это называется auto wiring
Нет. Почитай доки что-ли для начала. Если непонятно, могу объяснить.
Tom>·>Циклические зависимости делать не надо. Никогда. А если это действительно неизбежно — это должно быть сложно и сразу явно видно. Ты можешь случайно добавить незаметив, а контейнер тебе всё сам свяжет и не пикнет, втихую добавив +100 к техническому долгу. А потом эту вязанку развязывать. Tom>Да действительно контейнер в случае циклической зависимости упадёт только во время резолва. У меня такая проблема встречалась один раз и выявлена была в тестах. Ничего серьёзного, решилась за пару часов.
В случае использования lazy и property injection этот самый auto-wiring всё втихую сошьёт.
Tom>·>public class RequestModule и пусть он и управляет временем жизни создаваемых им IDisposable-ы ровно так как надо, а не универсальный всемогутер, который делает какую-то неявную магию. Tom>Не понял я кто такой RequestModule и как он может определить какой обьект надо диспоузить а какой нет.
RequestModule это контекст создаваемый для реквеста. В нём и будет нужные new внутри using. Он не должен ничего определять, ты сам пишешь код так, чтобы всё что надо диспозилось когда надо.
Tom>·>Я знаю. Сам таким был. Tom>·>Потом попал в компанию, где история развития была такая: "что попало как попало" -> "монстр Spring Framework" -> "модный Guice" -> "DI, plain Java code". По началу тоже возмущался отстутсвием "современного" контейнера, а потом осенило. Tom>Я кстате заметил что ты из жабы+spring пришёл по опасениям за конфиг+xml. Tom>Сейчас контейнеры совсем лругие. Это уже не монстры а достаточно простые, быстрые и логичные девайсы. Tom>Советую перестать их бояться, хотя бы попробовать не нескольких проектах. Tom>Многое становиться делать проще и удобнее чем руками.
guice именно такой, там вообще нет xml-конфигов. Но всё равно фтопку.
spring4 тоже теперь умеет xml-free.
Tom>Ничего контейнер не прячем, зависимости у каждого класса явные. Если их много, то эта проблема и так явная и контейнер тут не причём.
Я говорю не о классе, а о крупных модулях/блоках приложения.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, Cyberax, Вы писали:
C>Я внутри Amazon'а работаю в облачных вычислениях. Самый эпицентр service-based архитектуры, можно сказать. Языки разработки — Java и производные от неё.
[Немного оффтопика]
AWS? А правда говорят, что это единственное нормальное подразделение во всём Амазоне в плане условий работы?
Мы тут с коллегами, человек 12, год назад дружно работу меняли — получили гринкарты и пошли искать повышений на стороне. Я подумывал в Амазон пойти, но в другое подразделение. Но как-то не сложилось — на собеседование сходил, но оффер не получил. Потом с коллегами говорил, оказалось что я был единственным, кто и в самом деле хотел туда пойти, остальные хотели получить оффер Амазона чтоб потом выторговать себе побольше денег в других конторах. Очень ругали work/life ballance Амазона, говорят что народ там живёт на работе и единственное место с человеческими условиями работы — разработка AWS.
Здравствуйте, Lexey, Вы писали:
L>Может у тебя специфика такая. У меня, как правило, ситуация обратная. Юнит-тесты писать проще и дешевле, чем интеграционные. И исполняются они на порядки быстрее. А бывало и так, что хороший интеграционный тест в принципе невозможен. И самое интересное вылезает в процессе пуско-наладки.
Я извиняюсь, что подымаю старую тему. Но в случае юнит-тестов — ситуация — все тесты зеленые, а оно не работает — вполне себе. А в процессе пуско-наладки оно не работает и как выясняется — работать не могло, по причине, например отсутствия самого важного куска в SQL процедуре.
Интеграционные тесты в каком-то смысле необходимо воспринимать как функциональные, например:
1. Компилятор для определенного входа, должен генерировать определенный код; или выдавать строго определенные ошибки в сторого определенных случаях/позициях.
2. Браузер должен ренедерить pixel-to-pixel картинку для заранее определенных/подготовленных данных.
Это: легко реализуется, ну и именно в том числе такие подходы используются в реальных проектах.
Плюсы очевидны — мы работаем на вполне определенных входных данных, кстати, что важно — они вполне читаемы человеком (повезло).
Безусловно есть совершенно другой класс систем, где входы/выходы и состояние настолько запрятаны, что протестировать так просто... просто не получится. Или входы/выходы едва анализируются человеком.
Ну вот пример на который натыкался лично я: процессинг карт/транзакций на самом деле за 20 лет никогда не считал комиссию по терминалу в соответствии со спецификацией — она должна была быть взята в соответствии с приказом на терминал на дату транзакции. На самом деле считалось по последнему приказу (точнее в соответствии с приказом соответствующий текущей дате). В случае задержки обработки по тем или иным причинам реальный разрыв времени / сбой может произойти. Ситуация как уже понятно весьма и весьма редкая, тем не менее поизошла.
И тут мы имеем, допустим классику: и сервер приложений, и терминальный сервер и SQL сервер и даже где-то в стороне ОДБ.
Так вот, пока мы не наделим всей информацией все подсистемы для моделирования одного такого кейса — смоделировать по сути не получится.
Как только мы начнем наделять *каждую* подсистему необходимыми данными перед проведением теста — тест можно будет выполнить с минимальным количеством проверок / иногда даже не заглядывая во все подсистемы. В данном случае — всё вообще можно было сделать со стороны терминального сервера эмулируя команды и карт и других процессингов, что в итоге может быть проще — а может быть и нет (зависит от тестовой инфраструктуры).
Детали я маленько забыл, давно с этим уже не работаю.
И если бы я это делал изначально — я забил бы болт на все, и максимально обложил бы тестами как раз с этой стороны — потому что в момент переписывания одной из подсистем делал в общем-то тоже самое, только в меньшем объеме, вдобавок вскрыл моменты которые спецификацией не определены, что не могло бы быть незамеченным, если бы такие тесты проводились.
Безусловно это требует усилий, и немалых, однако большинство из них просто технические, но главный — это как раз создание вменяемых сценариев и вменяемое воплощение их в кодируемый вид (для меня прямой кодинг для C# не работал от слова вообще — слишком много шума, DSL получше — но тут тоже дорожка тонкая — захочется отойти в сторону — он будет или слишком сложным или вернемся к C#).
А, ещё был опыт с моками и везде DI. Подход/тесты выходили настолько дебильные и большие, что легче бы сделали интеграционные — вдобавок система *полностью* наблюдаема через одну точку доступа, и фэйковый поставщик данных. Вдобавок прямо перед этим я в своем проекте делал именно так, и подход себя зарекомендовал вполне сносно. Толку явно больше, потому как довольно просто описывается, вместо кода для моков в которых опять таки можно ошибиться, а затем допиливание самого кода что бы оно могло быть протестировано моками. Фе.
Сейчас у меня (не мой личный, скорее неофициально прижившийся) — фокус на фичи, и смотрим на error rate и выборчно результаты (их можно глазами осознать / довольно легко проверить), может быть несколько деплоев в день. Впрочем для кое-каких подсистем, пришлось написать свои интеграционные тесты. Совсем мало юнит. Впрочем для тестов — и кастом/MITM прокси, позволяющая "украсть" ответ запроса и потом бесконечно его тыркать (для перфоманс тестов), да и небольшой in-house фреймворк. Кое-где я писал. Просто, мы, как и многие зависим от third party практически константно — и важно, что оно или работает в обозначенных сценариях — или нет. Юниты тут как бы тоже никчему не прилепишь, да и выполнить практически нормальный workflow проще. Скорее тесты — по необходимости, но не более, или там где я ожидаю изменение поведения в будущем, и хочу быть уверен, что функциональный блок сохранит работоспособность, вплоть до ассерта в рантайме. Ну, а встреченных проблем кстати в third party — хватает, начиная от самого фреймворка, заканчивания каким-нибудь клиентом к rabbit который в асинхронном методе синхронно лочится в BlockingCollection, чем ставит раком абсолютно всю систему, хотя и не должен бы. Кватовая физика прямо — попытки пронаблюдать состояние в дебаггере — приводят к тому, что tasks "отлипают".
Я в сущности согласен, что всё — зависит от специфики. Самое важное — знать спецификацию/сценарии, с этим у меня обычно проблемы — их ведь обычно нет и сценарии нужно придумывать — а это и есть самое сложное/затратное (имхо), для любых видов тестирования.
PS: Кстати сейчас проект практически без DI. Но кое-где напрашивается.
Update: Кстати, есть один немаловажный момент, — если система (программный комплекс) предполагает долгую жизнь, то интеграционные тесты (в случае их хорошего исполнения) позволяют полностью абстрагироваться от конкретной базы кода (понятно, что если это позволяет предмет, и в необходимых количествах), что сыграет наруку в будущем. Таким образом при необходимости миграции с продукта версии 1 к версии 2 (в современном версионировании браузеров — от перехода от версии к 70 к версии 230), где версия >200 по праву считается совершенно отдельным и независимым продуктом (т.е. переписан с "нуля", стал например распределенным, или же использует несовместимое хранилище (т.е. нет возможности апгрейда, есть возможность конверсии, при чем не всегда обратимой)) — интеграционные/функциональные тесты помогут ответить на вопрос клиента: не станет ли всё раком после перехода. А программиты которые работали над версиией 200 — смогут ответить себе на вопрос — будет ли это точно работать именно у этого клиента. Поскольку я лично с этим столкнулся, наверное поэтому считаю, что приоритет для интеграционных и функциональных тестов, по минимум юнит, оставляя им место, где просмотрев код — неясно работает ли он. Это обычно всякие парсеры и/или мудреная/перераспухшая логика, и/или где неясны граничные моменты а пощупать их хочется (и есть возможность). Впрочем перераспухей логики скорее всего как раз юнит и не помогут.
Проекты с DI, дебажить сложно и в них сложно разбираться.
Понаписывают рантайм подмену интерфесов, но на практике вообще не встречал что бы в рантайме делали подмену одних сервисов на другие. Подмену можно было сделать напрямую в коде без всякого DI.
DI однозначно отлично подойдет для приложений где приложение устанавливается к потребителю локально и потребитель сам пишет для себя плагины, исспользуя указанное апи. (вариант: приложение — это веб сервик к которому пишут плагины сторонние люди, отпадает по секурити.)
Для сервисной архитектуры, по тестам: нафиг моки. Только усложняют жизнь и вводят в заблуждение. Надо тестировать на реальных данных, тестируя реальный код, а не код с костылями и бинтами. Сгенерил бд с тестовыми данными со скриптов. Прогнал по юнит и интеграционным тестам, почитал логи — увидел что апи соответствует спеки и норм.
Підтримати Україну у боротьбі з країною-терористом.
Здравствуйте, #John, Вы писали:
J>Проекты с DI, дебажить сложно и в них сложно разбираться. J>Понаписывают рантайм подмену интерфесов, но на практике вообще не встречал что бы в рантайме делали подмену одних сервисов на другие. Подмену можно было сделать напрямую
Я не понимаю, где этот источник невежества? Удивительно, что ты не первый это несёшь. Каким образом DI связанно с интерфейсами? Каким образом DI связан с runtime-подменой? Ведь даже если взять wiki, там ничего такого нет.
Вот ты лично об этом откуда узал, что для DI нужны интерфейсы или runtime-подмена?
J> в коде без всякого DI.
Как именно?
J>DI однозначно отлично подойдет для приложений где приложение устанавливается к потребителю локально и потребитель сам пишет для себя плагины, исспользуя указанное апи. (вариант: приложение — это веб сервик к которому пишут плагины сторонние люди, отпадает по секурити.)
Так DI или контейнеры?
J>Для сервисной архитектуры, по тестам: нафиг моки. Только усложняют жизнь и вводят в заблуждение. Надо тестировать на реальных данных, тестируя реальный код, а не код с костылями и бинтами. Сгенерил бд с тестовыми данными со скриптов. Прогнал по юнит и интеграционным тестам,
Такие тесты как правило трудно-подъёмные, медленные и плохо локализуют ошибку. Но такие тесты тоже нужны.
И вообще, юнит-тесты это больше инструмент разработки, а не контроля качества.
J>почитал логи — увидел что апи соответствует спеки и норм.
Что ещё за логи??
У правильного теста два состояния: pass/fail.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, fddima, Вы писали:
F> Update: Кстати, есть один немаловажный момент, — если система (программный комплекс) предполагает долгую жизнь, то интеграционные тесты (в случае их хорошего исполнения) позволяют полностью абстрагироваться от конкретной базы кода (понятно, что если это позволяет предмет, и в необходимых количествах), что сыграет наруку в будущем.
Интеграционные и юнит-тесты друг-друга не заменяют. Оба вида нужны и важны.
У меня был опыт в образцово-показательном проекте где было то, и то.
Юнит-тестов было несколько десятков тысяч, выполнялись они за 1-3 минуты на машине разработчика, поэтому перед коммитом они прогонялись всегда. Большее число багов отлавливалось именно ЮТ.
Интеграционных было около тысячи. Выполнялись они около 20 минут, обычно разработчики их запускали выборочно, лихорадочно правя если что-то поломалось в CI.
Ещё были функциональные тесты (которые ты называешь интеграционными), абстрагированые от базы кода, т.е. взаимодействуют с системой по тем же каналам, что и реальные клиенты — WebUI, REST, FIX, sockets, etc. Вот их было около восьми тысяч и выполнялись они около 20 минут на кластере из ~150 машин. Выполнить все на машине разработчика — нереально. Поэтому без предварительного багофильтра в виде ЮТ и ИТ они бы вечно были красными и бесполезными. А так падение ФТ в CI было событием, если в течение 5 минут поправить ошибку не можешь, твой коммит ревертят.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, fddima, Вы писали:
F> Я извиняюсь, что подымаю старую тему. Но в случае юнит-тестов — ситуация — все тесты зеленые, а оно не работает — вполне себе. А в процессе пуско-наладки оно не работает и как выясняется — работать не могло, по причине, например отсутствия самого важного куска в SQL процедуре.
Ну, это банальщина. На такую фигню, как процедуры в базе, можно и юнит-тесты написать.
В моем примере все намного веселее: нужно активно взаимодействовать с промышленной установкой, которая делается партнерами в единичном экземпляре под конкретного заказчика. Аналогичный стенд для тестирования сделать просто невозможно, ибо установка стоит дофига, плюс количество ее компонентов и система автоматики меняется от поставки к поставке. Сильно упрощенный программно-аппаратный эмулятор есть, но он покрывает очень ограниченное число вариантов. Вот и получается, что создать среду для полноценных интеграционных тестов просто невозможно.
Здравствуйте, ·, Вы писали:
·>Здравствуйте, #John, Вы писали:
J>>Проекты с DI, дебажить сложно и в них сложно разбираться. J>>Понаписывают рантайм подмену интерфесов, но на практике вообще не встречал что бы в рантайме делали подмену одних сервисов на другие. Подмену можно было сделать напрямую ·>Я не понимаю, где этот источник невежества? Удивительно, что ты не первый это несёшь. Каким образом DI связанно с интерфейсами? Каким образом DI связан с runtime-подменой? Ведь даже если взять wiki, там ничего такого нет. ·>Вот ты лично об этом откуда узал, что для DI нужны интерфейсы или runtime-подмена?
Осмелюсь предположить из того г...кода откуда один из DI фреймворков пришлось отхреначивать.
J>> в коде без всякого DI. ·>Как именно?
Иногда достаточно одного if
J>>DI однозначно отлично подойдет для приложений где приложение устанавливается к потребителю локально и потребитель сам пишет для себя плагины, исспользуя указанное апи. (вариант: приложение — это веб сервик к которому пишут плагины сторонние люди, отпадает по секурити.) ·>Так DI или контейнеры?
Вы так эмоционально холиварите в топике "О наивном DI"... Имхо не стоило ждать здесь борьбы за рассовую чистоту DI, здесь рассматривается совсем другой аспект. Возможно аспект неграмотный (с точки зрения тех кто хорошо разбирается в теме DI и давно его использует), но уж какой есть. Имхо нельзя игнорировать тот факт, что из за базза поднятого вокруг DI его пытаются использовать не так и не там игнорируя негативные результаты.
Вот в чем проблема и смысл топика, а вовсе не в том, что люди которым приходится отхреначивать DI из провальных проектов как-то не очень правильно DI понимают.
Здравствуйте, Lexey, Вы писали:
L>Ну, это банальщина. На такую фигню, как процедуры в базе, можно и юнит-тесты написать.
Можно теоретически. На практике под капотом интерпретатор поданных аргументов в зависимости от настроек в БД и фаз лун. А логика размазана для неподготовленного чела по 10/100mb sql сорсов (два проекта с которыми я работал), в реальности конечно если знать где искать, — тысяч 20 строк sql лапши (sp, триггеры, кучи таблиц, я имею ввиду которые учавствуют), в которой можно разобраться — но по факту — задача невыполнима в разумные сроки. Реверс кода быстро показывает, что он делает что-то именно так — проверку например по незадокументированному флагу. Опечатка ли, так задумано и лог VSS — ясности не приносит, спросить не у кого. Можно просто заниматься реверс инжинирингом для написания тестов, но совершенно не прогрессировать в бизнес задаче и в тестах, т.к. что бы сделать тесты — придется понять задачу и... Ну вот и поди реши что писать и на что хватит сил.
Я собственно говорю, что когда коду более 10 лет — он легко обрастает потом и кровью добытыми фичами, и естественно — не всегда об этом известно. Написали юнит — хорошо, но и мешаться они не должны.
Понятно, что это пожалуй единичные случаи. Тем не менее, — для простых процедур никогда не было необходимости в тестах.
L>Вот и получается, что создать среду для полноценных интеграционных тестов просто невозможно.
Да, это интересно. Ну, специфика... что ж поделать. Я и не говорил что "мой" путь единственно верный. Опять же, не поварившись в проекте — не знаешь слабые (и необходимые для тестов) места и/или подход. Ну а покрывать все — по мне — расточительно. Опять же — вопрос в допустимых затратах и доступных (человеческих и временных) ресурсах. Да и вообще были ли тесты или поле непаханое.
Скорость и легкость запуска безусловно важна, и надо прилагать усилия, что бы это сохранялось. Да блин, не всегда проект вообще можно запустить после чекаута/сборки без каких-то шизанутых плясок с конфигом. А другие упорно заставляют запускать студию от админа, хотя этого можно избежать.
Понятно, что девелопер не сможет развернуть гигабайтную базу (терабайтную) без геморов. Но в больших/уникальных проектах один фиг свои правила. Я не верю, что проблему скорости совсем невозможно преодолеть. Да и я очень подозреваю, что абсолютно все тесты не нужны сразу (девелоперу).
А в целом поинт понятен / принимается.
--
Я думаю, что не нужно писать 300 строк инфраструктурного кода который настраивает моки, что бы проверить 200 строк простейшего кода. Ровно как и не нужны они в pass-thru коде коего на самом деле завались в проектах.
Но вообще каждый (проект) сам себе должен выбирать подходящие правила/соглашения. Проблема только как выбрать походящие,.. в смысле пока кто-нить не набросит на вентилятор — опытом никто не делится.
Здравствуйте, ·, Вы писали:
·> Каким образом DI связанно с интерфейсами?
DI — это же, у нас есть конструктор, который в параметре принимается интерфейс/класс, а после исспользует его методы. А дальше мы пишем код: если класс в конструкторе принимает такой-то класс, то заменика ты нам в такой-то момент времени, это класс на вот этот. >>Каким образом DI связан с runtime-подменой?
Да, не обязательно подменять в рантайме, но зачем его подменять в компилируемом коде? когда можно и так передать в параметр конструктора нужный объект. ·>Вот ты лично об этом откуда узал, что для DI нужны интерфейсы или runtime-подмена?
Вот видел как его в проектах применяли не для подмены в рантайме. Но оно его подменяло как раз в рантайме.
J>> в коде без всякого DI. ·>Как именно?
Не применять насследование вообще. только фукнциональщина. Хз. почему но в последнее время замечаю чем меньше в коде ооп,(вместо наследования — вложенные объекты). Тем код становится более гибким и адаптируемый под измечивые требования заказчика.
·>И вообще, юнит-тесты это больше инструмент разработки, а не контроля качества.
Да.
>>У правильного теста два состояния: pass/fail.
Да. ·>Что ещё за логи??
Можно тестировать апи так: скрипт посылает json на /api/user/dropbyme, получает json.
И приложение все http request|responce сливает в лог файл. Иногда туда попадают ошибки, связанные с добавлением нового функционала. Или напр. когда мануально тестят тоже можно в логах увидеть где произошла ошибка, разобрать лог, пересоздать бд с данными, запустить скрипт для тестирования метода, послать туда данные из лога и восспроизвести ошибку.
Підтримати Україну у боротьбі з країною-терористом.
Здравствуйте, #John, Вы писали:
J>·> Каким образом DI связанно с интерфейсами? J>DI — это же, у нас есть конструктор, который в параметре принимается интерфейс/класс, а после исспользует его методы. А дальше мы пишем код: если класс в конструкторе принимает такой-то класс, то заменика ты нам в такой-то момент времени, это класс на вот этот.
И где ты об этом прочитал? Можно ссылочку?
>>>Каким образом DI связан с runtime-подменой? J>Да, не обязательно подменять в рантайме, но зачем его подменять в компилируемом коде? когда можно и так передать в параметр конструктора нужный объект.
Не знаю что и где ты подменяешь, но с DI это никак не связано.
J>·>Вот ты лично об этом откуда узал, что для DI нужны интерфейсы или runtime-подмена? J>Вот видел как его в проектах применяли не для подмены в рантайме. Но оно его подменяло как раз в рантайме.
Кто на ком стоял? Можно пример?
J>>> в коде без всякого DI. J>·>Как именно? J>Не применять насследование вообще.
Оппа. Теперь ещё и наследование к DI приплели. ОТКУДА ЭТО ВСЁ БЕРЁТСЯ?!
J>только фукнциональщина. Хз. почему но в последнее время замечаю чем меньше в коде ооп,(вместо наследования — вложенные объекты). Тем код становится более гибким и адаптируемый под измечивые требования заказчика.
А ещё у меня сегодня поезд задержали, пришлось с пересадкой ехать. Явно всё из-за DI. Как там бузина в огороде?
J>·>Что ещё за логи?? J>Можно тестировать апи так: скрипт посылает json на /api/user/dropbyme, получает json. J>И приложение все http request|responce сливает в лог файл. Иногда туда попадают ошибки, связанные с добавлением нового функционала.
Тесты значит кривые. Падать тесты должны, а не то что кто-то случайно заметил неожиданную ошибку в логе. Как вообще в гигабайтных логах можно что-то заметить — непонятно.
J>Или напр. когда мануально тестят тоже можно в логах увидеть где произошла ошибка, разобрать лог, пересоздать бд с данными, запустить скрипт для тестирования метода, послать туда данные из лога и восспроизвести ошибку.
Обычно логи читают когда тест упал и разбираются в причине. Или, иногда, когда пишут новый тест. Зачем читать логи после прогона тестов?
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Three types of dependency injection
There are at least three ways an object can receive a reference to an external module:
constructor injection: the dependencies are provided through a class constructor.
setter injection: the client exposes a setter method that the injector uses to inject the dependency.
interface injection: the dependency provides an injector method that will inject the dependency into any client passed to it. Clients must implement an interface that exposes a setter method that accepts the dependency.
·>Не знаю что и где ты подменяешь, но с DI это никак не связано. ·>Можно пример?
Вот о одном из проектов(исспользуется autofac):
В Global.asax регистрируется тип который будет создаваться через рефлексию(а значит в рантайме):
И зачем все эти сложности с интерфейсами, рефлексией? почему бы просто не написать: new UserManager().Save(user);
Типа у нас когда-то в далеком будущем появится еще один UserManager2 и что бы не рефакторить код мы подправим
одну строчку в Global.asax ?
·>Обычно логи читают когда тест упал и разбираются в причине. Или, иногда, когда пишут новый тест. Зачем читать логи после прогона тестов?
Да все верно, логи смотреть если тест сломался или у пользователся отображаются неверные данные.
Підтримати Україну у боротьбі з країною-терористом.
J>Three types of dependency injection
J>There are at least three ways an object can receive a reference to an external module:
J>constructor injection: the dependencies are provided through a class constructor.
J>setter injection: the client exposes a setter method that the injector uses to inject the dependency.
J>interface injection: the dependency provides an injector method that will inject the dependency into any client passed to it. Clients must implement an interface that exposes a setter method that accepts the dependency.
Слово interface таки нашел? А ты прочитал что такое Interface Injection, как оно устроено? Каким образом оно связано с IUserManager+UserManager?
И где тут что-либо связанное с рантайм-подменой?
J>·>Не знаю что и где ты подменяешь, но с DI это никак не связано. J>·>Можно пример? J>Вот о одном из проектов(исспользуется autofac): J>В Global.asax регистрируется тип который будет создаваться через рефлексию(а значит в рантайме):
Это не DI. Это Singleton + Service Locator, да ещё и использование какого-то очередного IoC-контейнера (что собственно и громко написано на https://autofac.org/).
Слово "Register" сразу должно намекать что используется registry. А тут даже явно противопоставляют DI: "The registry makes the code more difficult to maintain (opposed to using Dependency injection), because it becomes unclear when you would be introducing a breaking change."
Мне интересен источник невежества. Каким образом у тебя в голове этот код ассоциируется именно с DI?
J>И зачем все эти сложности с интерфейсами, рефлексией? почему бы просто не написать: new UserManager().Save(user);
Ок. Давай по шагам. Где именно ты хочешь это написать? И что делать с зависимостями самого UserManager? Например, ему может понадобиться DbConnection и SomeUserConfigParams, а DbConnection тянет за собой ConnectionParams.
J>Типа у нас когда-то в далеком будущем появится еще один UserManager2 и что бы не рефакторить код мы подправим J>одну строчку в Global.asax ?
Неужели autofac не умеет регистировать классы без интерфейсов? Возьмите другой, наверняка другие умеют. А лучше вообще выкиньте эту бяку.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, ·, Вы писали:
·>Мне интересен источник невежества. Каким образом у тебя в голове этот код ассоциируется именно с DI?
Ок, перечитаю. J>>И зачем все эти сложности с интерфейсами, рефлексией? почему бы просто не написать: new UserManager().Save(user); ·>Ок. Давай по шагам. Где именно ты хочешь это написать? И что делать с зависимостями самого UserManager? Например, ему может понадобиться DbConnection и SomeUserConfigParams, а DbConnection тянет за собой ConnectionParams.
Допустим проект(другой) из двух либ:
1. Там где находятся MVC котроллеры,Global.asax.
[HttpPost]
[ActionName("save")]
public ResponceResult Save(UserModel user){
var s = new UserService();
return s.Save(user);
}
2. Вторая либа Domain:
Где находятся *Service *Managers. UserService имеет обслужываеющий код(логирование, ловлю ошибок, создания ResponceResult), простую бизнес логику, и UserService дергает методы UserManage для извлечения данных из бд, выполнения sql процедур.
public class UserService {
public ResponceResult Save(UserModel usermodel){
try
{
if (!usermodel.IsValid())
{
return new ResponceResult { ... };
}
using (var someContext = new SomeContext())
{
var userManager =new UsersManager(ibeaconContext);
var users = userManager.List();
...
return new ResponceResult() {... };
}
}
catch (Exception ex)
{
return new ResponceResult { ...};
}
}
#endregion
Как передать SomeUserConfigParams .
Так как либа должна быть независимой от либы которая ее исспользует.
Надо в либе Domain создать класс для разных пользовательских настроек(которые исспользуются в этой либе).
class Settigs{
public SomeUserConfigParams {get;set;}
public SomeOtherThings {get;set;}
}
Создать Singleton с этим объектом в Domain либе.
И теперь при создании new UserService.Save() метода как-то инициализировать Singleton.
Підтримати Україну у боротьбі з країною-терористом.
Здравствуйте, #John, Вы писали:
J>>>И зачем все эти сложности с интерфейсами, рефлексией? почему бы просто не написать: new UserManager().Save(user); J>·>Ок. Давай по шагам. Где именно ты хочешь это написать? И что делать с зависимостями самого UserManager? Например, ему может понадобиться DbConnection и SomeUserConfigParams, а DbConnection тянет за собой ConnectionParams. J>Допустим проект(другой) из двух либ:
Ещё хуже.
J>1. Там где находятся MVC котроллеры,Global.asax. J>
J> var s = new UserService();
J> return s.Save(user);
J>
И какой тут смысл создавать инстанс UserService? Чем UserService.Save со статическим методом хуже?
J>2. Вторая либа Domain: J>Где находятся *Service *Managers. UserService имеет обслужываеющий код(логирование, ловлю ошибок, создания ResponceResult), простую бизнес логику, и UserService дергает методы UserManage для извлечения данных из бд, выполнения sql процедур. J>
J> using (var someContext = new SomeContext())
J> {
J> var userManager =new UsersManager(ibeaconContext);
Не понял. Что за ibeaconContext? Где DbConnection и ConnectionParams?
J>Как передать SomeUserConfigParams . J>Создать Singleton с этим объектом в Domain либе. J>И теперь при создании new UserService.Save() метода как-то инициализировать Singleton.
Т.е. у тебя все зависимости будут синглтонами. Правильно понял? Жуть же. Типичный спагетти-код и радости дебага правильности порядка инициализации синглтонов.
А теперь посмотрим на DI здорового человека:
public class UserRestController
{
private const UserService userService;
public UserRestController(UserService userService) {this.userService = userService;}
[HttpPost]
[ActionName("save")]
public ResponceResult Save(UserModel user){
return userService.Save(user);
}
}
public class UserService {
private const DbConnection connection;
private const UserManager userManager;
public UserService(DbConnection connection, UserManager userManager) {this.connection = connection; this.userManager = userManager;}
public ResponceResult Save(UserModel usermodel){
try
{
if (!usermodel.IsValid())
{
return new ResponceResult { ... };
}
using (var txn = new TransactionContext(connection))
{
var users = userManager.List();
...
/// validate data, save data.
txn.commit();
return new ResponceResult() {... };
}
}
catch (Exception ex)
{
return new ResponceResult { ...};
}
}
}
И отдельно где-нибудь в Global делаем явный wiring:
var someUserConfigParams = readParamsFromConfigFile();
var connectionParams = readConnectionParamsFromAnotherConfigSource();
var dbConnection = new DbConnection(connectionParams);
var userManager = new UserManager(someUserConfigParams);
var userService = new UserService(dbConnection, userManager);
var userRestController = new UserRestController();
var HttpServer server = new HttpServer();
server.addHandler(userRestController);
server.start();
Почему редко такой код встречается?..
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, ·, Вы писали:
·>И какой тут смысл создавать инстанс UserService? Чем UserService.Save со статическим методом хуже?
Вообщем-то можно и статические методы исспользовать.
·>Не понял. Что за ibeaconContext? Где DbConnection и ConnectionParams?
Опечатка. ibeaconContext — это someContext. SomeContext — это наследованный от Entities из EF, который наследуется от DbContext.
ConnectionParams ему можно передать из Singleton объектов.
·>Т.е. у тебя все зависимости будут синглтонами. Правильно понял? Жуть же. Типичный спагетти-код и радости дебага правильности порядка инициализации синглтонов.
Есть такое.
·>А теперь посмотрим на DI здорового человека: ·>
·>public class UserService {
·> public UserService(DbConnection connection, UserManager userManager) {this.connection = connection; this.userManager = userManager;}
·>
·>И отдельно где-нибудь в Global делаем явный wiring: ·>
·>var userService = new UserService(dbConnection, userManager);
·>
·>Почему редко такой код встречается?..
А если в классе UserService в методе Save для бизнес логики надо исспользовать разные *Managers,
а в методе Delete еще более разные(UserProfileManager, AcitvityManager и т.д.).
Передавать в UserService(FabricOfManagers fabric) — фабрику классов со всеми менеджерами?
Підтримати Україну у боротьбі з країною-терористом.
Здравствуйте, #John, Вы писали:
J>·>И какой тут смысл создавать инстанс UserService? Чем UserService.Save со статическим методом хуже? J>Вообщем-то можно и статические методы исспользовать.
Зачем тогда классы вообще существуют?..
J>ConnectionParams ему можно передать из Singleton объектов.
Хррр. Имхо, это даже хуже чем IoC-контейнер.
J>·>Т.е. у тебя все зависимости будут синглтонами. Правильно понял? Жуть же. Типичный спагетти-код и радости дебага правильности порядка инициализации синглтонов. J>Есть такое.
И ещё получается такой некий твой "class Settigs", который зависит от всего и все от него зависят.
Собственно IoC-контейнер позволяет этим хоть как-то рулить. Синглтоны однозначно в топку.
J>·>Почему редко такой код встречается?.. J>А если в классе UserService в методе Save для бизнес логики надо исспользовать разные *Managers, J>а в методе Delete еще более разные(UserProfileManager, AcitvityManager и т.д.). J>Передавать в UserService(FabricOfManagers fabric) — фабрику классов со всеми менеджерами?
Нет. Просто передаёшь всех менеджеров в конструкторе.
UserService(UserProfileManager, AcitvityManager, SomethingManager, ...)
Да, конструктор _может_ получиться с тучей аргументов, но зато сразу видно что именно требуется для функционирования данного класса, тебе не нужно читать код каждого отдельного метода чтобы выяснить а что же этот конкретный метод в этой ветке if-условия решил вытянуть из глобального Context.Current. И, более того, это всё проверяется на этапе компиляции, все зависимости обеспечены, плюс IDE помогает с навигацией по коду, find usages, go to declaration и прочим.
Далее, если у тебя таки получается класс у которого очень много зависимостей это сразу явно видно по его конструктору. Это просто значит что данный класс превращается в Универсальный Всемогутор (https://en.wikipedia.org/wiki/God_object) и требуется рефакторинг: пилишь компоненты по зависимостям так, чтобы уменьшить максимальную валентность графа зависимостей.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[11]: О "наивном" DI и об архитектурном бессилии
Полный сарказма ответ:
J>>·>И какой тут смысл создавать инстанс UserService? Чем UserService.Save со статическим методом хуже? J>>Вообщем-то можно и статические методы исспользовать. ·>Зачем тогда классы вообще существуют?..
Чтобы упрощать структуру кода, там где это может быть полезно... не?
J>>·>Т.е. у тебя все зависимости будут синглтонами. Правильно понял? Жуть же. Типичный спагетти-код и радости дебага правильности порядка инициализации синглтонов. J>>Есть такое. ·>И ещё получается такой некий твой "class Settigs", который зависит от всего и все от него зависят. ·>Собственно IoC-контейнер позволяет этим хоть как-то рулить. Синглтоны однозначно в топку.
Старая история, IoC-контейнер выполняет роль статического конструктора, но адепты верят, что так они чем-то по особенному рулят.
J>>·>Почему редко такой код встречается?.. J>>А если в классе UserService в методе Save для бизнес логики надо исспользовать разные *Managers, J>>а в методе Delete еще более разные(UserProfileManager, AcitvityManager и т.д.). J>>Передавать в UserService(FabricOfManagers fabric) — фабрику классов со всеми менеджерами? ·>Нет. Просто передаёшь всех менеджеров в конструкторе. ·>UserService(UserProfileManager, AcitvityManager, SomethingManager, ...) ·>Да, конструктор _может_ получиться с тучей аргументов, но зато сразу видно что именно требуется для функционирования данного класса, тебе не нужно читать код каждого отдельного метода чтобы выяснить а что же этот конкретный метод в этой ветке if-условия решил вытянуть из глобального Context.Current.
Это иллюзия, код каждого конкретного метода читать все равно придется
·>И, более того, это всё проверяется на этапе компиляции, все зависимости обеспечены, плюс IDE помогает с навигацией по коду, find usages, go to declaration и прочим.
А все остальные способы создания сервисов в коде на этапе компиляции не проверяются, зависимости не обеспечивают, а IDE будет намеренно мешать навигации по коду!
·>Далее, если у тебя таки получается класс у которого очень много зависимостей это сразу явно видно по его конструктору. Это просто значит что данный класс превращается в Универсальный Всемогутор (https://en.wikipedia.org/wiki/God_object) и требуется рефакторинг: пилишь компоненты по зависимостям так, чтобы уменьшить максимальную валентность графа зависимостей.
Какая красота, люди ведь не замечают, что полдня работают с одним файлом кол-во строк которого перевалило за несколько тыс. И что найти метод без средств студии уже просто нереально А так — глянул конструктор и все понятно А то, что всю эту сервисную обвязку при рефакторинге скорее всего придется переносить во новые классы. Ну это фигня не упаримся. Нам работу компилятора выполнять не в тягость!