Автор начал про TDD, а рассказывает про "написал херовый код, как мне его теперь протестировать" и предлагает в качестве фикса тестирование реализации одного конкретного метода, а не целого поведения, ради которого создавался этот класс, что добавляет хрупкости и не приносит никакой пользы.
Кстати, про хрупкость таких тестов не сказано ни слова, а это гораздо большая проблема, чем то, о чем с претензией на тотальный срыв покровов говорится в статье.
Человек за 15 лет практики не понял, что TDD — это способ избежать появления такого кода и таких тестов.
я свято придерживался принципов TDD
Ну понятно, такой же ***, как и те, кого он приводит в качестве примера. Фу.
P.S. Выше в комментах уже приводили эти доводы, но мне хотелось дополнить их выводом, выделенным курсивом. И простите за жесткость. Заколебало.
Репозиторий Core. Core чего, фреймворка? Но он же слабосвязанный? И что во фреймворке такого, центрального, без чего фреймворк — не фреймворк?
О нет, это ядро всего приложения… Фреймворк не должен быть ядром приложения, ядро должно быть независимым ни от чего. Чисто практически — кто будет использовать интерфейсы или наследовать свои классы от никому не известной 3d-party библиотеки?
Storages/KeyValueStorage. Финт с null-овым именем для вызова мощного деструктивного поведения когда-нибудь дорого обойдется. Явное лучше неявного, почему бы не сделать методы loadAll/saveAll()?
Load data from external storage into this object by specified...? То ли впопыхах вкоряченный и забытый костыль, то ли тотальное непонимание ООП. От KeyValue клиентам нужно только одно — положить данные, а потом их забрать, им глубоко фиолетово, что там где-то за KeyValue есть еще одно external storage. Хинт: попробуйте реализовать этот интерфейс на базе memcached.
/**
* Stores the value by specified key in this object
* @param int|string|null $key
* @param mixed $value
*/
public function set($key, $value);
Ключи "1" и 1 — будут считаться за один и тот же ключ или за разные? Укажите string, стандартного приведения типов достаточно.
Зачем Вам XxxStorageAwareInterface? Здесь не проглядывается ни одной причины для его использования. Выглядит особенно странно с учетом Вашего прохладного отношения к DI. Кстати, аннотации противоречат сигнатурам методов.
SingletonInterface, SingletonTrait. Нас настойчиво подталкивают к использованию синглтонов. В нормальном фреймворке должно быть легко делать правильные вещи и тяжело — неправильные.
SingletonTraitTest. Проверять рефлекшеном, что конструктор непубличный — это, пардон, днище. Класс нельзя инстанцировать через new — вот что надо проверять, не больше и не меньше. И дайте тестовым методам нормальные имена хотя бы.
Больше не смотрел. Общее впечатление — куча неважных, да еще спорных мелочей (типа FI), неочевидное поведение, везде наследование и магия, непонимание базовых принципов ООП, косяки на всех уровнях абстракции. Как это вообще можно выкладывать?
> Возможно есть другие, более удобные способы регулирования времени
Да, есть один очень хороший способ — не завязываться намертво на недетерминированные неконтролируемые зависимости, и больше не пытаться исправить то, что исправлять не следует.
Это не ООП. И вся статья про то, как мимикрировать под ООП, не задействовав сути идеи и ее преимуществ.
> мы наглядно и полноценно продемонстрировали все принципы объектно-ориентированного программирования… инкапсуляция… наследование… полиморфизм.
Нет, не все. Нет самого главного — абстракции.
> (в комментах) Я только не понял в чем разница — передавать свойство Canvas или весь объект Image
В этом ВСЯ разница. В данном случае — НАДО передавать Canvas, потому что Canvas в Delphi — это более универсальная сущность, чем Image. Передадите Image — сможете рисовать только на Image, передадите Canvas — на Image и на других типах, имеющих Canvas.
И это все равно будет не ООП. Чтобы сделать это ООП (именно в этом месте) — создайте интерфейс с методами рисования и передавайте его в то, что хочет отрисоваться (фигуру) и реализуйте в том, на чем хотите отрисоваться — в данном случае в Image на базе методов того же Canvas'а. Читайте про Dependency Inversion Principle.
> инкапсуляция
Image.Canvas — вот вы и разрушили всю инкапсуляцию. Читайте про что-нибудь вроде «avoid getters», в т.ч. у упомянутых здесь Мартина и компании. Something.Anything — это намного хуже, чем геттеры.
> полиморфизм представлен, например, так: есть базовый абстрактный класс Кошачьи, от которого порождены многочисленные наследники
Про наследование лучше просто забудьте. Ищите по кивордам «composition over inheritance».
P.S. Настоятельно рекомендую ознакомиться хотя бы со страницами с результатами поиска по ключевым словам выше (например avoid getters). Вы будете удивлены тем, насколько далеко то, что предлагает стандартный дельфийский подход, от ООП.
> Мой рабочий день всегда начинается с просмотра сайтов, чтобы быть в курсе последних бед и катастроф, от которых страдает наш мир, но сегодня одну из этих бед я обнаружил в своем ящике электронной почты. Это было очередное письмо от рекрутера.
На что жалуется этот мистер? Все же по его плану идет.
Личное мнение: жалобы на рекрутеров — признак завышенной самооценки и привлечение внимания к себе. И еще это такой тренд.
На джей-двигателе не в каждой версии можно было между планетами летать. И таргоны хорошо фигачились технологическим лазером (который для майнинга), потому что он из-за низкой скорострельности (при достаточной мощности) меньше сажал батареи :).
P.S. Ну это я так, продемонстировать причастность к тому прекрасному времени :)
Нет у меня никакого коммерческого интереса. Я обычный, когда-то близорукий (-7, астигматизм, боли в глазах после часа за компом и все остальные прелести) айтишник, в один момент ставший нормальным человеком. Это Вам так кажется, что очки и линзы — это просто. Это нифига не просто, это огромный ущерб ощущению себя и мира, и чтобы это понять, надо обрести стопроцентное зрение без внешних девайсов.
> А лазерную коррекцию, глядя на цену и список побочек придумали исключительно филантропы.
Не надо сравнивать фемтолазеры и продукт уровня «очки нннадо». За первое и заплатить не жалко людям, которые это дело придумали, реализовали, протестировали и сделали доступным в виде «пришел с 0.3, ушел с 1».
Если говорить более предметно, то бесплатным и без побочных эффектов не бывает ничего. В Вашем комментарии зацепил уровень аргументации — «один важный человек сказал» и «зато очки полезны тем, что...» + высосанное из пальца утверждение. Хотя полно нормальных аргументов за и против и для лазерной коррекции, и для ношения очков.
Т.е. полгодика сидеть слушать препода, а потом показать настоящий компьютер? Почему бы просто сразу не объяснять, как делать правильно, а как — нет?
Статья — полный бред.
Автор начал про TDD, а рассказывает про "написал херовый код, как мне его теперь протестировать" и предлагает в качестве фикса тестирование реализации одного конкретного метода, а не целого поведения, ради которого создавался этот класс, что добавляет хрупкости и не приносит никакой пользы.
Кстати, про хрупкость таких тестов не сказано ни слова, а это гораздо большая проблема, чем то, о чем с претензией на тотальный срыв покровов говорится в статье.
Человек за 15 лет практики не понял, что TDD — это способ избежать появления такого кода и таких тестов.
Ну понятно, такой же ***, как и те, кого он приводит в качестве примера. Фу.
P.S. Выше в комментах уже приводили эти доводы, но мне хотелось дополнить их выводом, выделенным курсивом. И простите за жесткость. Заколебало.
Заглянул на Гитхаб...
Репозиторий Core. Core чего, фреймворка? Но он же слабосвязанный? И что во фреймворке такого, центрального, без чего фреймворк — не фреймворк?
О нет, это ядро всего приложения… Фреймворк не должен быть ядром приложения, ядро должно быть независимым ни от чего. Чисто практически — кто будет использовать интерфейсы или наследовать свои классы от никому не известной 3d-party библиотеки?
Storages/KeyValueStorage. Финт с null-овым именем для вызова мощного деструктивного поведения когда-нибудь дорого обойдется. Явное лучше неявного, почему бы не сделать методы loadAll/saveAll()?
Load data from external storage into this object by specified...? То ли впопыхах вкоряченный и забытый костыль, то ли тотальное непонимание ООП. От KeyValue клиентам нужно только одно — положить данные, а потом их забрать, им глубоко фиолетово, что там где-то за KeyValue есть еще одно external storage. Хинт: попробуйте реализовать этот интерфейс на базе memcached.
Ключи "1" и 1 — будут считаться за один и тот же ключ или за разные? Укажите string, стандартного приведения типов достаточно.
Зачем Вам XxxStorageAwareInterface? Здесь не проглядывается ни одной причины для его использования. Выглядит особенно странно с учетом Вашего прохладного отношения к DI. Кстати, аннотации противоречат сигнатурам методов.
SingletonInterface, SingletonTrait. Нас настойчиво подталкивают к использованию синглтонов. В нормальном фреймворке должно быть легко делать правильные вещи и тяжело — неправильные.
SingletonTraitTest. Проверять рефлекшеном, что конструктор непубличный — это, пардон, днище. Класс нельзя инстанцировать через new — вот что надо проверять, не больше и не меньше. И дайте тестовым методам нормальные имена хотя бы.
Больше не смотрел. Общее впечатление — куча неважных, да еще спорных мелочей (типа FI), неочевидное поведение, везде наследование и магия, непонимание базовых принципов ООП, косяки на всех уровнях абстракции. Как это вообще можно выкладывать?
Вы не разобрались.
Ничего не мешает. В рантайме будет один класс, в котором явно все прописано.
Да, есть один очень хороший способ — не завязываться намертво на недетерминированные неконтролируемые зависимости, и больше не пытаться исправить то, что исправлять не следует.
j_wayne выше все правильно сказал.
— это и есть s=vt. Абстракции и интерфейсы к ним — основа практического ООП.
Это не ООП. И вся статья про то, как мимикрировать под ООП, не задействовав сути идеи и ее преимуществ.
> мы наглядно и полноценно продемонстрировали все принципы объектно-ориентированного программирования… инкапсуляция… наследование… полиморфизм.
Нет, не все. Нет самого главного — абстракции.
> (в комментах) Я только не понял в чем разница — передавать свойство Canvas или весь объект Image
В этом ВСЯ разница. В данном случае — НАДО передавать Canvas, потому что Canvas в Delphi — это более универсальная сущность, чем Image. Передадите Image — сможете рисовать только на Image, передадите Canvas — на Image и на других типах, имеющих Canvas.
И это все равно будет не ООП. Чтобы сделать это ООП (именно в этом месте) — создайте интерфейс с методами рисования и передавайте его в то, что хочет отрисоваться (фигуру) и реализуйте в том, на чем хотите отрисоваться — в данном случае в Image на базе методов того же Canvas'а. Читайте про Dependency Inversion Principle.
> инкапсуляция
Image.Canvas — вот вы и разрушили всю инкапсуляцию. Читайте про что-нибудь вроде «avoid getters», в т.ч. у упомянутых здесь Мартина и компании. Something.Anything — это намного хуже, чем геттеры.
> полиморфизм представлен, например, так: есть базовый абстрактный класс Кошачьи, от которого порождены многочисленные наследники
Про наследование лучше просто забудьте. Ищите по кивордам «composition over inheritance».
P.S. Настоятельно рекомендую ознакомиться хотя бы со страницами с результатами поиска по ключевым словам выше (например avoid getters). Вы будете удивлены тем, насколько далеко то, что предлагает стандартный дельфийский подход, от ООП.
На что жалуется этот мистер? Все же по его плану идет.
Личное мнение: жалобы на рекрутеров — признак завышенной самооценки и привлечение внимания к себе. И еще это такой тренд.
> фреймворка для unit-тестирования Android-приложений — Robolectric
То, что описано в статье, юнит-тестированием не является.
Бонусом к этому способу идет соблюдение SRP, нормальное юнит-тестирование и все такое.
А то я за всю жизнь не посмотрел ни одной серии японских мультиков и из (около)предметной области знаю только словосочетание «Сейлор Мун».
(Шутка, конечно, но только отчасти! :)
P.S. Ну это я так, продемонстировать причастность к тому прекрасному времени :)
Эта на первый взгляд серебрянная пуля полна таких подвохов, что ручные джойны на клиенте покажутся сказкой.
>Было бы не плохо чтобы была возможность произвольно задавать индексы для данных,
Так они прекрасно и задаются так, как Вам надо в Монге. Или что-то другое имеется ввиду?
> нужно дополнительно вытаскивать актеров — в MongoDB есть lookup (хотя появился не так давно)
Да ладно, ему сто лет в обед. И нормальным решением это не считается.
> могли взять nosql где есть джойны, например OriendDB/ArangoDB, либо хранить актеров в кеше.
… и все это является нарушением парадигмы Монги и аналогичных хранилищ — хранить данные так, как вы собираетесь их читать.
Нисколько не защищая МонгоДБ, отмечу, что все критикующие ее статьи не сообщают, как делать правильно, если (условно) нужны джойны.
А правильно прибегать к денормализации данных (точнее, усиливать заложенную в такие БД денормализацию). Понимая, конечно, чего это стоит.
Не надо сравнивать фемтолазеры и продукт уровня «очки нннадо». За первое и заплатить не жалко людям, которые это дело придумали, реализовали, протестировали и сделали доступным в виде «пришел с 0.3, ушел с 1».
Если говорить более предметно, то бесплатным и без побочных эффектов не бывает ничего. В Вашем комментарии зацепил уровень аргументации — «один важный человек сказал» и «зато очки полезны тем, что...» + высосанное из пальца утверждение. Хотя полно нормальных аргументов за и против и для лазерной коррекции, и для ношения очков.