В разработке через тестирование (TDD) вы начинаете с неоптимального решения, а затем итеративно создаете лучшие, добавляя тестовые примеры и рефакторинг. Предполагается, что шаги будут небольшими, а это означает, что каждое новое решение будет как-то соседствовать с предыдущим.
Это напоминает математические методы локальной оптимизации, такие как градиентный спуск или локальный поиск. Хорошо известным ограничением таких методов является то, что они не гарантируют нахождение глобального оптимума или даже приемлемого локального оптимума. Если ваша отправная точка отделена от всех приемлемых решений большой областью плохих решений, попасть туда невозможно и метод потерпит неудачу.
Чтобы быть более конкретным: я думаю о сценарии, в котором вы реализовали несколько тестовых случаев, а затем обнаружили, что следующий тестовый случай потребует совершенно другого подхода. Вам придется выбросить свою предыдущую работу и начать все сначала.
Эта мысль может быть применена ко всем гибким методам, которые выполняются небольшими шагами, а не только к TDD. Есть ли в этой предложенной аналогии между TDD и локальной оптимизацией серьезные недостатки?
источник
Ответы:
Чтобы сделать ваше сравнение более адекватным: для каких-то проблем алгоритмы итеративной оптимизации, скорее всего, дадут хорошие локальные оптимумы, а в некоторых других ситуациях они могут потерпеть неудачу.
Я могу представить себе ситуацию, когда это может произойти в реальности: когда вы выбираете неправильную архитектуру таким образом, вам необходимо заново воссоздать все существующие тесты с нуля. Допустим, вы начинаете реализовывать свои первые 20 тестовых примеров на языке программирования X в операционной системе A. К сожалению, требование 21 включает в себя необходимость выполнения всей программы в операционной системе B, где X недоступно. Таким образом, вам нужно отбросить большую часть своей работы и переопределения на языке Y. (Конечно, вы не выбросили бы код полностью, а перенесли бы его на новый язык и систему.)
Это учит нас, даже при использовании TDD, это хорошая идея, чтобы сделать общий анализ и дизайн заранее. Это, однако, также верно для любого другого подхода, поэтому я не рассматриваю это как внутреннюю проблему TDD. И для большинства реальных задач программирования вы можете просто выбрать стандартную архитектуру (например, язык программирования X, операционная система Y, система баз данных Z на аппаратном XYZ), и вы можете быть относительно уверены, что итеративная или гибкая методология, такая как TDD не приведет вас в тупик.
Цитируя Роберта Харви: «Вы не можете вырастить архитектуру из модульных тестов». Или pdr: «TDD не только помогает мне прийти к лучшему окончательному дизайну, но и помогает получить меньше попыток».
Так на самом деле то, что вы написали
может стать реальностью - если вы выберете неправильную архитектуру, вы, скорее всего, не достигнете нужного решения оттуда.
С другой стороны, когда вы заранее проводите общее планирование и выбираете правильную архитектуру, использование TDD должно походить на запуск алгоритма итеративного поиска в области, где можно ожидать достижения «глобального максимума» (или, по крайней мере, достаточно хорошего максимума). ) в несколько циклов.
источник
Я не думаю, что у TDD есть проблема локальных максимумов. Код, который вы пишете, может, как вы правильно заметили, но именно поэтому рефакторинг (переписывание кода без изменения функциональности) на месте. По сути, по мере увеличения количества ваших тестов вы можете переписывать значительную часть вашей объектной модели, если вам нужно, сохраняя поведение неизменным благодаря тестам. Тесты устанавливают инвариантные истины о вашей системе, которые, следовательно, должны быть действительными как в локальных, так и в абсолютных максимумах.
Если вас интересуют проблемы, связанные с TDD, я могу упомянуть три разных вопроса, о которых я часто думаю:
Проблема полноты : сколько тестов необходимо, чтобы полностью описать систему? Является ли «кодирование примерами» полным способом описания системы?
Проблема закалки : к любому интерфейсу тестов необходим неизменный интерфейс. Помните, тесты представляют собой инвариантные истины . К сожалению, эти истины вообще не известны для большей части кода, который мы пишем, в лучшем случае только для внешних объектов.
Проблема тестового повреждения : чтобы сделать утверждения тестируемыми, нам может потребоваться написать неоптимальный код (например, менее производительный). Как мы пишем тесты, чтобы код был настолько хорош, насколько это возможно?
Отредактировано с учетом комментария: вот пример исключения локального максимума для «двойной» функции посредством рефакторинга
Тест 1: когда вход равен 0, вернуть ноль
Реализация:
Рефакторинг: не требуется
Тест 2: когда ввод 1, вернуть 2
Реализация:
Рефакторинг: не требуется
Тест 3: когда на входе 2, вернуть 4
Реализация:
Рефакторинг:
источник
То, что вы описываете в математических терминах, это то, что мы называем изображением себя в углу. Это происшествие вряд ли является эксклюзивным для TDD. В водопаде вы можете собирать и проливать требования в течение нескольких месяцев, надеясь, что вы сможете увидеть глобальный максимум только для того, чтобы добраться туда и понять, что есть лучшая идея на следующем холме.
Разница в гибкой среде, в которой вы никогда не ожидали быть идеальной, поэтому вы более чем готовы бросить старую идею и перейти к новой.
Более конкретно, для TDD существует метод, позволяющий избежать этого при добавлении функций в TDD. Это предпосылка приоритета трансформации . Там, где у TDD есть формальный способ рефакторинга, это формальный способ добавления функций.
источник
В своем ответе @Sklivvz убедительно доказал, что проблемы не существует.
Я хочу утверждать, что это не имеет значения: фундаментальная предпосылка (и смысл) итеративных методологий в целом и Agile, и особенно TDD в частности, заключается в том, что не только глобальный оптимум, но и локальные оптимумы также не имеют смысла. т известно. Итак, другими словами: даже если бы это было проблемой, в любом случае нет способа сделать это итеративным способом. Предполагая, что вы принимаете основную предпосылку.
источник
Могут ли методы TDD и Agile обещать оптимальное решение? (Или даже «хорошее» решение?)
Не совсем. Но это не их цель.
Эти методы просто обеспечивают «безопасный переход» из одного состояния в другое, признавая, что изменения являются трудоемкими, трудными и рискованными. И смысл обоих методов заключается в том, чтобы приложение и код были жизнеспособными и проверенными на соответствие требованиям быстрее и более регулярно.
TDD фокусируется на обеспечении того, чтобы каждый «кусок» кода удовлетворял требованиям. В частности, это помогает гарантировать, что код отвечает ранее существующим требованиям, в отличие от того, чтобы позволить требованиям руководствоваться плохим кодированием. Но это не обещает, что реализация "оптимальна" в любом случае.
Что касается Agile процессов:
Ловкость не ищет оптимального решения ; просто рабочее решение с целью оптимизации ROI . Он обещает рабочее решение скорее раньше , чем позже ; не "оптимальный".
Но это нормально, потому что вопрос неправильный.
Оптимумы в разработке программного обеспечения - нечеткие, движущиеся цели. Требования, как правило, меняются и изобилуют секретами, которые возникают только из-за вашего смущения в конференц-зале, полном боссов вашего босса. А «внутреннее совершенство» архитектуры и кодирования решения оценивается разделенными и субъективными мнениями ваших коллег и вашего управляющего руководителя - никто из которых не может ничего знать о хорошем программном обеспечении.
По крайней мере, методы TDD и Agile признают трудности и пытаются оптимизировать две вещи, которые являются объективными и измеримыми: « Работает», «Не работает» и « Рано или поздно».
И даже если у нас есть «работающие» и «скорее» в качестве объективных показателей, ваша способность оптимизировать их в первую очередь зависит от навыков и опыта команды.
Вещи, которые вы могли бы истолковать как усилия по созданию оптимальных решений, включают такие вещи, как
так далее..
Будет ли каждая из этих вещей на самом деле приводить к оптимальным решениям - это еще один замечательный вопрос!
источник
Пока еще никто не добавил, что описанная вами «разработка TDD» очень абстрактна и нереальна. Это может быть так в математическом приложении, где вы оптимизируете алгоритм, но в бизнес-приложениях этого не так часто случается.
В реальном мире ваши тесты в основном выполняют и проверяют бизнес-правила:
Например, если клиент 30 лет не курит с женой и двумя детьми, категория «премиум» - «x» и т. Д.
Вы не собираетесь итеративно изменять механизм расчета премиум-статуса, пока он не будет исправлен в течение очень долгого времени - и почти наверняка нет, пока приложение работает;).
То, что вы на самом деле создали, - это сеть безопасности, так что при добавлении нового метода расчета для определенной категории клиентов все старые правила внезапно не нарушаются и дают неправильный ответ. Сеть безопасности еще более полезна, если первым шагом отладки является создание теста (или серии тестов), который воспроизводит ошибку до написания кода для исправления ошибки. Затем, через год, если кто-то случайно воссоздает исходную ошибку, модульный тест будет прерван до того, как код будет даже проверен. Да, TDD допускает одно: теперь вы можете с большой уверенностью выполнять рефакторинг и убирать вещи. но это не должно быть важной частью вашей работы.
источник
Я не думаю, что это мешает. В большинстве команд нет ни одного человека, способного предложить оптимальное решение, даже если вы написали его на доске. TDD / Agile не будет мешать им.
Многие проекты не требуют оптимальных решений, и те, которые делают, необходимое время, энергия и внимание будут сделаны в этой области. Как и все остальное, что мы стремимся построить, во-первых, заставить его работать. Тогда сделай это быстро. Вы можете сделать это с помощью своего рода прототипа, если производительность так важна, а затем перестроить все это с мудростью, полученной в результате многих итераций.
Это может произойти, но более вероятно, что это произойдет из-за боязни изменения сложных частей приложения. Отсутствие каких-либо тестов может создать большее чувство страха в этой области. Одним из преимуществ TDD и наличия набора тестов является то, что вы создали эту систему с мыслью, что ее нужно будет изменить. Когда вы с самого начала придумаете это монолитное оптимизированное решение, его будет очень сложно изменить.
Кроме того, поместите это в контекст вашей озабоченности по поводу недостаточной оптимизации, и вы не можете не тратить время на оптимизацию вещей, которых вам не следует иметь, и на создание негибких решений, потому что вы были слишком сосредоточены на их производительности.
источник
Может быть обманчиво применять математическую концепцию типа «локальный оптимум» к разработке программного обеспечения. Использование таких терминов делает разработку программного обеспечения гораздо более измеримой и научной, чем на самом деле. Даже если для кода существовал «оптимум», у нас нет способа его измерить и, следовательно, нет способа узнать, достигли ли мы его.
Гибкое движение действительно было реакцией против убеждения, что разработку программного обеспечения можно планировать и прогнозировать математическими методами. Хорошо это или плохо, разработка программного обеспечения больше похожа на ремесло, чем на науку.
источник
fibonacci
, что то , что я видел в качестве примера / учебника по TDD, является более или менее ложью. Я готов поспорить, что никто не «обнаружил» Фибоначчи или другие подобные серии с помощью TDD. Каждый начинает с того, что уже знает фибоначчи, которые обманывают. Если вы попытаетесь выяснить это с помощью TDD, вы, вероятно, дойдете до тупика, о котором спрашивал OP: вы никогда не сможете обобщить ряд, просто написав больше тестов и рефакторинг - вы должны применить математические рассуждения!