Re[58]: Что такое Dependency Rejection
От: · Великобритания  
Дата: 30.01.24 22:15
Оценка:
Здравствуйте, Pauel, Вы писали:

P>>>·>Не понял, чем отличается 1 от 2? "все виды" это разве не "удалить"+"выгрузить"?

P>>>Разница в слове "отложенные". У вас недавно была проблема с этим словом. Решилась?
P>·>У нас такой проблемы не было. Т.е. разницы нет. ЧТД.
P>Забавный аргумент — у вас чего то там не было Мощный заход, внушает!
Ты нафантазировал, что у нас есть проблемы с отложенными вычислениями. Какой ты аргумент ожидаешь на такие фантазии?

>> Причём тут обсуждаемый "инлайн nextFriday"? У тебя там Time.now будет константным, да?

P>Вы зачем то полезли в инлайн строк
Я пример привёл, что инлайн сам по себе ни о чём не говорит. Ладно, если тебе строки не устраивают, пусть будет list.length() — с инлайном тоже ничего не выйдет.

P>>>Джава компилятор на основании иммутабельности умеет вводить кое какие оптимизации. Строки — одна из таких вещей.

P>·>Допустим, и причём тут инлайн?
P>Это вы мне объясните, для чего вам понадобилось инлайнить строки. Это ж ваш аргумент был.
Я объяснил, что метод length инлайнить не выйдет, но это вовсе не оозначает, что это какое-то плохой дизайн и код куда-то как-то прибит.

P>Я вам показал, что с str.length() у нас всё близко к идеалу — компилятор и джит без вас умеют инлайнить не абы как, а практически в константу.

Пусть будет list.length(). Дались тебе эти константы, вообще к теме не относится.

P>>>"как то" Не как то, а вручную вместо работы компилятором и ИДЕ.

P>·>Это твои фантазии, или кривая IDE, или неумение ею пользоваться.
P>Вы пока так и не показали, как будете свой nextFriday инлайнить. Ваш последний аргумент — сбежать в сторону "заинлайни str.length()"
Ты так и не рассказал с какой целью инлайнить.

P>>>А протаскивание такого конфига — тот еще код. Особенно когда это все инжектится

P>·>"изменение конфига" != "протаскивание конфига".
P>Весело у вас — изменение конфига не является кодом,
Конфиг это какой-нибудь тупой key=value файлик или около того, или файл с сертом например. Это ты правда это считаешь кодом?

P>изменение дерева депенденсов не является кодом, добавление параметра в конструктор не является кодом...

Является, конечно. И ты это называешь изменением дизайна.

P>Что еще у вас кодом не является?

А что у вас не является изменением дизайна?

P>Зато интерфейс с аннотациями без реализации у вас почему то "ужос-ужос это код!!!!111111"

Опять какая-то твоя больная фантазия. Цитаты ведь не будет как всегда.

P>·>Он _вам_ ответил. Его ответ — другое решение, которое и в моём коде тоже реализуется тривиально. Его решение иногда не подойдёт в том, что у него будет latency spike на cache miss. А моё решение с таймером в твоём коде не реализуется, придётся всё везде перелопачивать. ЧТД — мой код универсальнее, не требует изменения дизайна на каждый чих.

P>Не нужно ничего перелопачивать — будет ровно такая же функция как у Синклера, только кеширование будет иначе сделано.
А ты вот потрудись код написать, и опять увидишь что погорячился.

P>В этом случае мы основную бл тестируем без изменений — эта функция вообще не изменится. Для неё как были тесты по таблице истинности, так и остались.

Ну да, у меня тоже. В чём у тебя возникли сложности-то?

P>>>Нет, не нужно — функция где БЛ никак не изменилась, ни внутри, ни снаружи. Изменилась исключительно интеграция.

P>·>Ага, но у тебя интеграции — на каждый вызов nextFriday из множества методов контроллеров. У меня интеграция wiring — ровно одна строчка.
P>Вам нужно в каждый компонент явно вписывать это как зависимость, и не ошибаться — где клиентское, где серверное, где текущее, где предзаписаное время.
И? Это всё покрыто легковесными тестами, с проверками ожиданий правильности источника времени.

P>>>В вашем случае вы перепахали внутрянку той самой функции

P>·>Ну вы тоже.
P>Нисколько. Сама функция осталась без изменений. Смотрите пример Синклера внимательно — он вызывает основную функцию, а не модифицирует её.
Ты либо его код не понял, либо мой. Основная функция расчётов у него "SlowlyCalculatePrevAndNextFridays", у меня "doSomeComputations".

Да, кстати, у его решения ещё один огромный недостаток. Его реализация неявно подразумевает, что передаваемый "DateTime dt" это DateTime.Now, т.е. плавно изменяющийся параметр, только тогда кеш будет пересчитываться раз в неделю. Кто-то видя сигнатуру nextFriday(DateTime dt) — запросто может позвать её с чем попало, выкашивая кеш в самые неожиданные моменты времени. Отличная такая грабля будущим поколениям, happy debugging. Более того, чтобы даже просто начать такую оптимизацию метода — надо будет исследовать все call sites и удостовериться что туда передаётся именно текущее время, иначе кеш может быть бессмысленным, а в большом проекте эта штука может жить в шаред либе и задача вообще практически неразрешимой становится.

В моём же случае, nextFriday() явно запрещает туда передать что-то не то. Знание об источнике времени тут есть явно. И такие оптимизации делать становится гораздо проще, с более предсказуемым импактом. Иными словами, мы знаем конкретный применённый аргумент ЧПФ и эту информацию фиксируем явно и можем использовать.

P>·>"Снаружи" это откуда? А проблема в том, что множество мест связки nextFriday с Time.now ты протестировать никикак не можешь. Ещё раз повторюсь: ЧПФ. У тебя его нет.

P>Буквально "вызываем метод с параметром x" — такого не будет. И не нужно. А все, что дает конкретные результаты — будет ровно как у вас.
Я это и не спрашивал. Я спрашивал "результат зависит от Time.now", а не от чего-то ещё. C Time.now ты не можешь получить "конкретный результат", по определению.

P>>>С каких пор низкоуровневые приседания стали проще?

P>·>Я не знаю что называешь низкоуровневыми приседаниями.
P>Посмотрите внимательно — Фаулер для компонента протаскивает не абы что, репозитории-кеши-итд, а интерфейс взаимодействия
Просто выражено в терминах явы-шарпов "интерфейс с одним методом". getTopRestaurants — это и есть репозиторий по сути. Для ts оно ок наверное, но в яву-шарп такое лучше не тащить.

P>·>Я правильно понял?

P>Чтото навроде, примерно так же и у вас.
Ок. Круто, договорились, наконец-то. Теперь расскажи, как в твоей голове согласуется "код без использования моков" и "mock<AnInterface>"?

P>>>
P>>>mock<AnInterface>().whenCalledWith('getTopRestaurants', 'ляляля').returns(restaurants)[/tt] 
P>>>

P>>>Что теперь, попросите показать как это всё типизуется?
P>·>Да, серьёзно, Интересно. Ведь в коде теста никакого AnInterface даже и нет
P>Есть, глаза разуйте. Фаулер конструирует этот самый интерфейс. А раз он есть, то можно и тип оформить.
Я не вижу, ткни в строчку кода. Откуда ты возьмёшь AnInterface тут?

>> , фаулер за типы топит, которые не export. Автодополнятор вставит строчку 'getTopRestaurants' сам? Найдёт|отрефакторит имя функции и определит типы ляляля параметров?

P>Найдет. Отрефакторит. Определит. В тайпскрипте есть внятный вывод типа, и вы можете вывести тип тупла аргументов метода по его имени, тип возвращаемого значения, итд и тд. А раз есть тип, то у нас будет статический контроль и whenCalledWith, и returns, и много чего другого.
Круто, если так. Но ни шарп, ни ява так не умеет. Там такое просто не прокатит.

P>>>Смотря что вам надо. Серебряную пулю — такого нет. Составить список требований, сделать ревью, предложить дизайн, сделать ревью, написать тесты, сделать ревью, написать код, сделать ревью, пофиксить баги, сделать ревью — по моему так всё и делается.

P>·>Именно. Вот это всё и ещё маленько — оно может давать гарантию. Тесты же гарантию давать не могут, или ты так своё умение варить кашу из топора демонстрируешь?
P>Без тестов гарантий никаких не будет.
Да и в той притче каши без топора таки не было.

P>Нужно всё вместе:

P>1 Код ревью не работает в том случае, когда его можно скипнуть
P>2 Тесты не работают, когда их можно обойти
P>Что бы добиваться качества, вам нужно решить обе этих проблемы.
Угу. И?

P>·>Опять у тебя какая-то особая терминология. Под запросом я имею в виду данные приходящие от клиентов через внешний апи. Вот то что условный curl посылает — это запрос.

P>А я вам про принимающую сторону. Серверное и текущее время вы получаете забесплатно.
Допустим. Вопрос-то как проверить, что именно нужный источник времени используется где-то там для расчёта каких-то результатов?

P>А клиентское и так придет в виде значения, если у вас в апи так прописано.

curl генерит клиентское время сам где-то там внутре.

P>·>Реальным клиентам пофиг что у вас за такие сценарии A...Z и когда они у вас гоняются. Им надо их сценарии.

P>Забавно — это вы всерьёз покрываете тестами левые сценарии? Или это вам видится, что все вокруг вас дураки?
Тестовые сценарии != реальные сценарии.

P>>>Зачем?

P>·>Чтобы можно было пальчиком тыкнуть где конкретно ты заблуждаешься.
P>Я так понимаю, хорошего примера у вас нет.
Примера чего?

P>>>Голословно. Любой большой кусок кода нужно в первую очередь тестировать непосредственно.

P>·>Зачем?
P>1. чем больше кода между замерами, тем хуже детализация ошибок
P>2. чем больше косвенность, тем вам труднее покрыть основные пути хоть с какой нибудь детализацией
Для этого и используют разделение на юниты и юнит-тестирование. А моками отрезаются комбинации деталей.

P>Это особенности тестов вообще. Потому и убирают лишнее из тестового кода, и тут два основных подхода, оба основаны на отделении эффектов и зависимостей:

P>1. моки — тот подход, который вам кажется единственно верным, т.е. обрезание, подмена зависимостей и эффектов
Не то, что единственно верный, но практически неизбежный в достаточно сложной программе.

P>Пример — обмазали моками и написали тест "таймер вызывается дважды с такими вот аргументами"

Тебе уже двое раз 30 раз повторили — что так делать не надо.

P>2. классический — вытеснение зависимостей и эффектов в код верхнего уровня

P>Пример "sin(x) = y"
Это работает только на хоумпейджах. Чуть сложнее и внезапно выяснится что там этих самых верхних уровней штук пять.

А 3. где? В Ложную Дилемму поиграть решил?

P>>>А вы предлагаете всё делать наоборот.

P>·>Конечно. Т.к. надо тестировать поведение, а не детали реализацию.
P>Тестируется как раз наблюдаемое поведение вычислителя фильтров. Вычислитель фильтров обычная чистая функция, в ней ничего военного нет — тестируем как любую чистую функцию.
Проблема в том, что ты её адекватно протетсировать не можешь. Ассертить имена приватных функций и буквальные тексты запроса — такие тесты — бессмысленные и беспощадные.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.