В книгах в основном пишут про простые приемы, профилактические, например явное преобразование типов, создание ловушек в коде, просто грамотное проектирование и ещё про кучу всяких простеньких приемов. Но с ростом проектов, этого всего уже не хватает, по крайней мере мне. Ошибок много и их причины очень сложно и долго ищутся. Связность кода растет и никак с этим бороться не получается.
Вот, очень хочется узнать, кто какие приемы или т.п. применяет в борьбе с ошибками или вообще в отладке, или на этапе проектирования...
27.03.07 16:17: Перенесено модератором из '.NET' — TK
Здравствуйте, _Mihail, Вы писали:
_M>В книгах в основном пишут про простые приемы, профилактические, например явное преобразование типов, создание ловушек в коде, просто грамотное проектирование и ещё про кучу всяких простеньких приемов. Но с ростом проектов, этого всего уже не хватает, по крайней мере мне. Ошибок много и их причины очень сложно и долго ищутся. Связность кода растет и никак с этим бороться не получается. _M>Вот, очень хочется узнать, кто какие приемы или т.п. применяет в борьбе с ошибками или вообще в отладке, или на этапе проектирования...
Здравствуйте, _Mihail, Вы писали:
_M>В книгах в основном пишут про простые приемы, профилактические, например явное преобразование типов, создание ловушек в коде, просто грамотное проектирование и ещё про кучу всяких простеньких приемов. Но с ростом проектов, этого всего уже не хватает, по крайней мере мне. Ошибок много и их причины очень сложно и долго ищутся. Связность кода растет и никак с этим бороться не получается. _M>Вот, очень хочется узнать, кто какие приемы или т.п. применяет в борьбе с ошибками или вообще в отладке, или на этапе проектирования...
Главное правило: чем раньше ошибка обнаруживается, тем легче ее исправить. Соответственно, плясать надо от этого правила. Именно из этого правила вытекают и ранняя интеграция, и юнит тесты и статическая типизация и прочие полезные в ловле ошибок приемы.
Здравствуйте, _Mihail, Вы писали:
_M>Вот, очень хочется узнать, кто какие приемы или т.п. применяет в борьбе с ошибками или вообще в отладке, или на этапе проектирования...
Языки программирования со статической типизацией и сборкой мусора (например, C#, Nemerle).
Инструменты для поиска ошибок и предупреждений в процессе создания кода (наподобие ReSharper'а).
Комментирование кода.
Code review, в том числе и основанный на автоматическом анализе кода (например, с помощью FxCop).
Рефакторинг. Замена длинных методов короткими, сложных — простыми, запутанных — понятными.
Валидация входных параметров методов и выбрасывание исключений наподобие ArgumentException.
Частое использование конструкций наподобие Debug.Assert для проверки инвариантов и согласованности логики кода.
Отсутствие преждевременной оптимизации, упор на надежность и понятность.
Уменьшение количества нисходящих приведений типов (особенно так называемых "безопасных" — как с оператором as в C#). Их замена на параметрический полиморфизм и pattern matching.
Написание исчерпывающих unit-test'ов (ни одно исправление или улучшение кода не должно происходить без добавления соответствующего теста). Их регулярный автоматический запуск и автоматический контроль покрытости кода.
Здравствуйте, ZevS, Вы писали:
ZS>Здравствуйте, _Mihail, Вы писали:
_M>>В книгах в основном пишут про простые приемы, профилактические, например явное преобразование типов, создание ловушек в коде, просто грамотное проектирование и ещё про кучу всяких простеньких приемов. Но с ростом проектов, этого всего уже не хватает, по крайней мере мне. Ошибок много и их причины очень сложно и долго ищутся. Связность кода растет и никак с этим бороться не получается. _M>>Вот, очень хочется узнать, кто какие приемы или т.п. применяет в борьбе с ошибками или вообще в отладке, или на этапе проектирования...
ZS>Юнит тесты.
Как минимум, регрессионное тестирование (без юнит-тестов и тестов функциональности теоретически можно обойтись) ZS>Постоянный рефакторинг.
Здравствуйте, _Mihail, Вы писали:
_M>В книгах в основном пишут про простые приемы, профилактические, например явное преобразование типов, создание ловушек в коде, просто грамотное проектирование и ещё про кучу всяких простеньких приемов. Но с ростом проектов, этого всего уже не хватает, по крайней мере мне. Ошибок много и их причины очень сложно и долго ищутся. Связность кода растет и никак с этим бороться не получается.
Значит неправильно пишешь с самого начала, просто архитектура неправильная. Иначе бы связность кода не росла. Впрочем, может у тебя что супер сложное, конечно.
Если можешь по написанной программе понять, что она делает без заглядываний в другие файлы, это хорошо.
У меня вот та же проблема, конкретно на C# когда пишу, вот ну немогу написать большое приложение. Постоянно оказывается что что-то забыл, что-то неуклюже написано и просто не укладывается в голове. Дошло до того, что когда мне, после нескольких месяцев C#, пришлось писать на не любимом мной C++ я просто почувствовал себя как в раю
Вообще, когда используешь C# и .NET постоянно используешь сразу несколько компонентов. Обязательно надо что-то куда-то создать, записать, передать интерфейс и т.п. Куча служебных действий, иногда создаётся впечатление, что вернулся в assembler. Всё время думаешь о коде, а не о программе.
Здравствуйте, Gajdalager, Вы писали:
G>Как минимум, регрессионное тестирование (без юнит-тестов и тестов функциональности теоретически можно обойтись) ZS>>Постоянный рефакторинг.
А на основе чего тогда осуществлять регрессионное тестирование? И как проверять, не допущена ли ошибка при рефакторинге?
Нет уж, одно с другим завязано.
V>Главное правило: чем раньше ошибка обнаруживается, тем легче ее исправить. Соответственно, плясать надо от этого правила. Именно из этого правила вытекают и ранняя интеграция, и юнит тесты и статическая типизация и прочие полезные в ловле ошибок приемы.
Почитал про юнит тесты — очень мощная вещь. Только возникают небольшой вопрос. Вот при описании создания юнит тестов всё время используют простенькие примеры, типа функций округления чисел, смены расширения файла и т.п. А вот как поступают в случае тестирования сложных методов, которые помимо входных параметров внутри пользуются какими-нибудь общедоступными объектами (и меняют их), и эти объекты так вот просто для каждого отдельного юнит теста не создашь, а если и создашь, то по времени это будет очень долго. Я так понимаю в этом случае помимо подстановки тестовых параметров в метод, нужно специально конструировать и всю остальную среду приложения в тестовом режиме, одну для всех юнит тестов, а потом тестирование методов проводить не по алфавиту, а писать какой-то "сценарий", учитывающий, что "среда" по мере прохождения тестов будет меняться... так поступают в подобных случаях?
Верно подмечено , но тут и кроется ответ . Если система так сильно связанна , то это ошибки проектирования. Необходимо стремиться к минимальной связанности , а как минимум проектировать модули уже готовые к тестированию
Лично меня пока что спасает "проверки времени выполнения" и Sanity Tests , тесты на основную функциональность.
вот тут пишут про юнит-тесты, но они хороши для бизнес-логики и прикладных программ.
если вы системный программист, и разрабатываете _систему_, покрыть ее простыми тестами невозможно.
Здравствуйте, _Mihail, Вы писали:
_M>Связность кода растет и никак с этим бороться не получается.
Значить, кто-то когда-то и неизвестно зачем лазит куда ему не надо.
Лекарство — плотные code review на потенциальних виновниках. С последующей профилактической (или пенициарной?) беседой. А вообще, "cvs annotate" выдумал не идиот...
Или же, ваш дизайн прогнивает. Пора рефакторить.
Если есть на это время — вы счастливчик.
Если нет — мне вас жалко.
__________
16.There is no cause so right that one cannot find a fool following it.
Здравствуйте, _Mihail, Вы писали:
V>>Главное правило: чем раньше ошибка обнаруживается, тем легче ее исправить. Соответственно, плясать надо от этого правила. Именно из этого правила вытекают и ранняя интеграция, и юнит тесты и статическая типизация и прочие полезные в ловле ошибок приемы.
_M>Почитал про юнит тесты — очень мощная вещь. Только возникают небольшой вопрос. Вот при описании создания юнит тестов всё время используют простенькие примеры, типа функций округления чисел, смены расширения файла и т.п. А вот как поступают в случае тестирования сложных методов, которые помимо входных параметров внутри пользуются какими-нибудь общедоступными объектами (и меняют их), и эти объекты так вот просто для каждого отдельного юнит теста не создашь, а если и создашь, то по времени это будет очень долго. Я так понимаю в этом случае помимо подстановки тестовых параметров в метод, нужно специально конструировать и всю остальную среду приложения в тестовом режиме, одну для всех юнит тестов, а потом тестирование методов проводить не по алфавиту, а писать какой-то "сценарий", учитывающий, что "среда" по мере прохождения тестов будет меняться... так поступают в подобных случаях?
Здравствуйте, degor, Вы писали:
D>вот тут пишут про юнит-тесты, но они хороши для бизнес-логики и прикладных программ. D>если вы системный программист, и разрабатываете _систему_, покрыть ее простыми тестами невозможно.
Это почему? В чем проблема тестировать куски системы (например алгоритмы, состояния ошибки)и проводить integration тесты уже системы в целом (например с использованием реального железа или устройства, если это допустим дрова)? Ибо без тестов нельзя быть уверенным ни в чем, правда с тестами тоже
Здравствуйте, 0xDEADBEEF, Вы писали:
DEA>Лекарство — плотные code review на потенциальних виновниках. С последующей профилактической (или пенициарной?) беседой.
code review — это очень полезно, но с упором найти и устранить проблемы,
а не поучить "виновников". Вменяемые люди, которых все же большинство,
сами видят что к чему и делают выводы, когда видят результаты review.
Если есть какие-то косяки, которые повторяются постоянно,
то надо разбираться с причинами. Причины могут и не зависить от конкретного кодера.
В общем, устраивать детский сад с профилактическими беседами после
review — это не конструктивно, а часто и вообще вредно.
Здравствуйте, degor, Вы писали:
D>вот тут пишут про юнит-тесты, но они хороши для бизнес-логики и прикладных программ. D>если вы системный программист, и разрабатываете _систему_, покрыть ее простыми тестами невозможно.
Здравствуйте, nikov, Вы писали:
N>Языки программирования со статической типизацией и сборкой мусора (например, C#, Nemerle). N>Инструменты для поиска ошибок и предупреждений в процессе создания кода (наподобие ReSharper'а). N>Комментирование кода. N>Code review, в том числе и основанный на автоматическом анализе кода (например, с помощью FxCop). N>Рефакторинг. Замена длинных методов короткими, сложных — простыми, запутанных — понятными.
Я бы еще уточнил, что важно просто причесывать код. Не сикрет, что когда пишешь конерктный участок, то порой важно просто продавить решение, чтобы онозаработало.
Важно после этого взять и "причесать" код так чтобы он был не только работающим, но и понятным, простым... короче как ты сказал.
N>Валидация входных параметров методов и выбрасывание исключений наподобие ArgumentException. N>Частое использование конструкций наподобие Debug.Assert для проверки инвариантов и согласованности логики кода. N>Отсутствие преждевременной оптимизации, упор на надежность и понятность. N>Уменьшение количества нисходящих приведений типов (особенно так называемых "безопасных" — как с оператором as в C#). Их замена на параметрический полиморфизм и pattern matching. N>Написание исчерпывающих unit-test'ов (ни одно исправление или улучшение кода не должно происходить без добавления соответствующего теста). Их регулярный автоматический запуск и автоматический контроль покрытости кода.
+ Я бы добавил бы это список:
1. Конечно же отладчик и другие средства интерактивной отладки (Imediate, редаткриование кода без остановки программы и т.п.).
2. Продумывание реализуемых подзадач. Потому как многие привыкая на простых задачках лепить все по наитию поступают так же и с более сложными вещами.
3. Применение библиотек и другие способы абстрагирования. В прочем уменьшение исходников это примерно о том же но другими словами.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, prVovik, Вы писали:
V>Главное правило: чем раньше ошибка обнаруживается, тем легче ее исправить. Соответственно, плясать надо от этого правила. Именно из этого правила вытекают и ранняя интеграция, и юнит тесты и статическая типизация и прочие полезные в ловле ошибок приемы.
Еще лучше просто их не допускать. Отсюда вытекают: планирование, проектирование, применение билилотек, самосовершенствование, примерение паттернов проектирования...
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, aka50, Вы писали:
A>Здравствуйте, degor, Вы писали:
D>>вот тут пишут про юнит-тесты, но они хороши для бизнес-логики и прикладных программ. D>>если вы системный программист, и разрабатываете _систему_, покрыть ее простыми тестами невозможно.
A>Это почему? В чем проблема тестировать куски системы (например алгоритмы, состояния ошибки)и проводить integration тесты уже системы в целом (например с использованием реального железа или устройства, если это допустим дрова)? Ибо без тестов нельзя быть уверенным ни в чем, правда с тестами тоже
не надо путать тесты и юнит-тесты. последние — одна из практик xp, которая, на мой взгляд, имеет очень ограниченное применение. о чем я и сказал
проблема тестирования системы в том, что мясо приходится на взаимодействие объектов, а не на сами объекты, простые и корректно выполняющие свои контракты.
Здравствуйте, bkat, Вы писали:
B>Здравствуйте, degor, Вы писали:
D>>вот тут пишут про юнит-тесты, но они хороши для бизнес-логики и прикладных программ. D>>если вы системный программист, и разрабатываете _систему_, покрыть ее простыми тестами невозможно.
B>Покрывай из сложными тестами. B>В чем проблема?
свежая мысль в сабже. мой поинт в том, что есть системы, использование для которых юнит-тестов неэффективно, так как основная сложность заключается в процессах использующих эти объекты и во взаимодействии.добавьте сюда асинхронность и многопоточность.
Здравствуйте, _Mihail, Вы писали:
_M>Почитал про юнит тесты — очень мощная вещь. Только возникают небольшой вопрос. Вот при описании создания юнит тестов всё время используют простенькие примеры, типа функций округления чисел, смены расширения файла и т.п. А вот как поступают в случае тестирования сложных методов, которые помимо входных параметров внутри пользуются какими-нибудь общедоступными объектами (и меняют их), и эти объекты так вот просто для каждого отдельного юнит теста не создашь, а если и создашь, то по времени это будет очень долго.
Вопрос очень правильный. Действительно, прогон полного набора тестов на сложной системе может занимать несколько часов. В идеале, тесты должны полностью воссоздавать все окружение, в котором работает система. Например, если используется база данных, то хорошие тесты должны уметь создать ее "с нуля", прогнав необходимые скрипты, заполнить данными и "погонять" в разных режимах (можно подумать, что это излишние требования, но зато, если завтра Вам понадобится поднять новый продакшн-сервер, Вы можете быть уверены, что для этого достаточно просто запустить скрипт).
При активной разработке желательно, чтобы билд собирался и тестировался в автоматическом режиме каждый час (это приблизительная оценка, которая на самом деле очень зависит от типа проекта). К сожалению, прогон "тяжелых" тестов может ограничить количество билдов до двух раз в сутки. И о проблеме, внесенной утром, разработчики могут узнать только в конце рабочего дня, когда уже может не хватать времени на адекватное ее исправление. Чтобы этого избежать, можно запускать настроить запуск полного набор тестов лишь один раз в сутки (например, ночью), а в течение рабочего дня запускать его облегченный вариант, который может отработать меньше чем за час, и оперативно отловить основные проблемы.
Ну и конечно, желательно иметь достаточно шустрый билд-сервер.
Здравствуйте, degor, Вы писали:
D>проблема тестирования системы в том, что мясо приходится на взаимодействие объектов, а не на сами объекты, простые и корректно выполняющие свои контракты.
Каким образом было обнаружено, что "сами объекты, простые и корректно выполняющие свои контракты"? unit test?
"взаимодействие объектов" — это отдельный вид тестирования перпендикулярный unit testам — integration tests...
И даже подходы другие (например в java просто JUnit не очень помогает в integration, там уже более тяжелая артиллерия
типа http://jakarta.apache.org/cactus или вообще тестирование на выделенном сервере автоматическими клиентами
или даже спец группой тестировщиков...
Здравствуйте, aka50, Вы писали:
A>Здравствуйте, degor, Вы писали:
D>>проблема тестирования системы в том, что мясо приходится на взаимодействие объектов, а не на сами объекты, простые и корректно выполняющие свои контракты.
A>Каким образом было обнаружено, что "сами объекты, простые и корректно выполняющие свои контракты"? unit test?
нет. объекты такие тупые, что были написаны правильно с нуля )). неправильности отловлены в процессе отладки.
A>"взаимодействие объектов" — это отдельный вид тестирования перпендикулярный unit testам — integration tests... A>И даже подходы другие (например в java просто JUnit не очень помогает в integration, там уже более тяжелая артиллерия
о чем и речь.
A>типа http://jakarta.apache.org/cactus или вообще тестирование на выделенном сервере автоматическими клиентами A>или даже спец группой тестировщиков...
неудачный пример. посмотрите, что тестирует джакарта — (Servlets, EJBs, Tag Libs, Filters, ...). никак не похоже на сложную интегрированную систему.
D>свежая мысль в сабже. мой поинт в том, что есть системы, использование для которых юнит-тестов неэффективно, так как основная сложность заключается в процессах использующих эти объекты и во взаимодействии.добавьте сюда асинхронность и многопоточность.
Дак это понятно...
Если я пишу функцию возведения в нулевую степень,
то смысла в юнит тестах и в самом нету.
В этом случае действительно гораздо важнее процессы, в которых эта функция используется
Извини за иронию, но не верю, что у тебя смысла в юнит тестах нету.
Если ты сможешь кратко описать, что ты делаешь, то может быть и поверю...
Здравствуйте, degor, Вы писали:
D>не надо путать тесты и юнит-тесты. последние — одна из практик xp, которая, на мой взгляд, имеет очень ограниченное применение. о чем я и сказал
юнит-тесты — это то, что советует XP, но возникло это задолго до самого XP.
юнит-тесты — это часть так называемой V модели, в которой,
user requirements соответствуют приемочному тестированию (acceptance test)
system requirements — системному тестированию
спецификации модулей – модульному/unit тестированию
Здравствуйте, bkat, Вы писали:
B>Извини за иронию, но не верю, что у тебя смысла в юнит тестах нету. B>Если ты сможешь кратко описать, что ты делаешь, то может быть и поверю...
юнит-тесты и обвязка для них пишутся самим программистом. то есть качество тестов <= качество кода.
юнит-тесты проверяют работу отдельных частей системы, а не их взаимодействие.
юнит-тесты оправдывают себя только при непрерывном изменении элементов программы.
я готов признать пользу от юнит-тестов для regression testing, но не всегда эта польза окупается затратами на их разработку.
и наконец, я скептически отношусь к xp и юнит-тестам в частности.
Здравствуйте, bkat, Вы писали:
B>юнит-тесты — это то, что советует XP, но возникло это задолго до самого XP.
ok. я имел в виду юнит-тесты в том виде, в котором они применяются в xp.
B>юнит-тесты — это часть так называемой V модели, в которой, B>user requirements соответствуют приемочному тестированию (acceptance test) B>system requirements — системному тестированию B>спецификации модулей – модульному/unit тестированию
все это больше применимо к разработке прикладных программ. я же говорю о системах, эти программы поддерживающих — среды выполнения, операционные системы.
там, несомненно, тоже применимо юнит-тестиронвание (в его не экстремальном виде). но значение его много меньше.
Здравствуйте, degor, Вы писали:
D>Здравствуйте, bkat, Вы писали:
B>>Извини за иронию, но не верю, что у тебя смысла в юнит тестах нету. B>>Если ты сможешь кратко описать, что ты делаешь, то может быть и поверю...
То, что ты пишешь, это не описание того, что ты делаешь,
а описание границ применимости этого вида тестирования.
D>юнит-тесты и обвязка для них пишутся самим программистом. то есть качество тестов <= качество кода.
Качество тестов зависит от качества спецификаций модулей.
Его (качество) тоже можно контролировать.
D>юнит-тесты проверяют работу отдельных частей системы, а не их взаимодействие.
Конечно unit test не является integration test. Кто-то утверждает обратное?
unit test так же не заменит системного и многих других видов тестирования.
Впрочем я соглашусь, что юнит-тесты, как и любая работа, требует усилий.
Качество вообще на шару не получается.
Если конкретно в вашем случае польза меньше, чем затраты, то видимо вам это и не нужно...
Но мой опыт таков, что внедрение юнит-тестов всегда оправдывало себя.
Затраты, на самом деле, не велики, а польза есть.
У нас, к примеру, до внедрения юнит-тестов каждый программер все равно писал
какие-то тестовые программы, в которых игрался с тем, что он разрабатывает.
Т.е. каждый программер все равно создавал свою среду, в которой он мог
как-то убедиться в том, что его кусок в целом работает.
Внедрение юнит-тестов — это не более чем,
внесение порядка и предсказуемости в такую деятельность.
Здравствуйте, degor, Вы писали:
B>>юнит-тесты — это часть так называемой V модели, в которой, B>>user requirements соответствуют приемочному тестированию (acceptance test) B>>system requirements — системному тестированию B>>спецификации модулей – модульному/unit тестированию D>все это больше применимо к разработке прикладных программ. я же говорю о системах, эти программы поддерживающих — среды выполнения, операционные системы.
Прекращай героизацию системного программирования
Там есть свои особенности, но в целом там применимы все известные
подходы разработки программных продуктов.
Здравствуйте, bkat, Вы писали:
B>То, что ты пишешь, это не описание того, что ты делаешь, B>а описание границ применимости этого вида тестирования.
вот именно. у метода есть границы применимости, и эффективность применения в разных типах проектов. об этом надо помнить, а то получится как с рефакторингом ))
D>>юнит-тесты и обвязка для них пишутся самим программистом. то есть качество тестов <= качество кода. B>Качество тестов зависит от качества спецификаций модулей. B>Его (качество) тоже можно контролировать.
D>>юнит-тесты проверяют работу отдельных частей системы, а не их взаимодействие.
B>Конечно unit test не является integration test. Кто-то утверждает обратное? B>unit test так же не заменит системного и многих других видов тестирования.
будем считать, что договорились?
B>Впрочем я соглашусь, что юнит-тесты, как и любая работа, требует усилий. B>Качество вообще на шару не получается. B>Если конкретно в вашем случае польза меньше, чем затраты, то видимо вам это и не нужно... B>Но мой опыт таков, что внедрение юнит-тестов всегда оправдывало себя. B>Затраты, на самом деле, не велики, а польза есть. B>У нас, к примеру, до внедрения юнит-тестов каждый программер все равно писал B>какие-то тестовые программы, в которых игрался с тем, что он разрабатывает. B>Т.е. каждый программер все равно создавал свою среду, в которой он мог B>как-то убедиться в том, что его кусок в целом работает. B>Внедрение юнит-тестов — это не более чем, B>внесение порядка и предсказуемости в такую деятельность.
и что, организован процесс автоматического тестирования?
Здравствуйте, degor, Вы писали:
B>>Внедрение юнит-тестов — это не более чем, B>>внесение порядка и предсказуемости в такую деятельность. D>и что, организован процесс автоматического тестирования?
Ну да. Это уже дело техники, включить прогон тестов в Build.
Каждую ночь гоняются все тесты и к утру все кому надо получают письма с логами.
Здравствуйте, nikov, Вы писали:
N>В идеале, тесты должны полностью воссоздавать все окружение, в котором работает система. Например, если используется база данных, то хорошие тесты должны уметь создать ее "с нуля", прогнав необходимые скрипты, заполнить данными и "погонять" в разных режимах
Такие тесты уже на юнит тесты мало похожи и если я правильно понял это называется тестированием интеграции. Сейчас пишу такой тест, получается что-то типа "виртуального пользователя", который совершает все возможные действия с программой, начиная "с чистого листа". Только результаты выполнения каждой команды сверять смысла нет, да и сложно это. Просто если во время выполнения какой-нибудь команды возникает ошибка (а в методах много проверок входных параметров, проверок промежуточных результатов), то всё это дело останавливается. Вроде нормально получается, я на правильном пути?
Здравствуйте, 0xDEADBEEF, Вы писали:
DEA>Здравствуйте, _Mihail, Вы писали:
_M>>Связность кода растет и никак с этим бороться не получается. DEA>Значить, кто-то когда-то и неизвестно зачем лазит куда ему не надо.
DEA>Лекарство — плотные code review на потенциальних виновниках. С последующей профилактической (или пенициарной?) беседой. А вообще, "cvs annotate" выдумал не идиот...
А если потенциальный виновник только один? Или скажем два? Мне самому с собой проводить "пенециарную" беседу?
DEA>Или же, ваш дизайн прогнивает. Пора рефакторить. DEA>Если есть на это время — вы счастливчик. DEA>Если нет — мне вас жалко.
Эх, вот почему у меня все программы на C# сразу гнилые получаются? Даже те, которые в 500 строчек? Вот ну не работают, хоть ты тресни. Просто не разработка, а сплошной рефакторинг. А ведь сначала, на "бумаге", всё нормально выглядит, даже лучше, чем обычно
Re[2]: Ребят, я понимаю, что вам смешно, но проблема правда
Здравствуйте, FDSC, Вы писали:
FDS>Вообще, когда используешь C# и .NET постоянно используешь сразу несколько компонентов. Обязательно надо что-то куда-то создать, записать, передать интерфейс и т.п. Куча служебных действий, иногда создаётся впечатление, что вернулся в assembler. Всё время думаешь о коде, а не о программе.
Вместо того, чтобы смеяться, лучше бы подсказали, что я не так делаю... а то я уже скоро на Nemerle и Scheme начну писать лучше, чем на C# .
Здравствуйте, bkat, Вы писали:
B>Здравствуйте, degor, Вы писали:
B>>>Внедрение юнит-тестов — это не более чем, B>>>внесение порядка и предсказуемости в такую деятельность. D>>и что, организован процесс автоматического тестирования?
B>Ну да. Это уже дело техники, включить прогон тестов в Build. B>Каждую ночь гоняются все тесты и к утру все кому надо получают письма с логами.
причем логи должны писаться и самими модулями, а не только тестами.
т.е. проект должен иметь достаточно мощную и удобную подсистему логирования.
Re[10]: Ошибок не делает тот, кто ничего не делает
Здравствуйте, sc, Вы писали:
sc>причем логи должны писаться и самими модулями, а не только тестами. sc>т.е. проект должен иметь достаточно мощную и удобную подсистему логирования.
А что понимается под "удобной" системой логирования?
Я имею ввиду, какие функции должны быть у этой системы, что бы она была удобной?
Здравствуйте, degor, Вы писали:
D>вот тут пишут про юнит-тесты, но они хороши для бизнес-логики и прикладных программ. D>если вы системный программист, и разрабатываете _систему_, покрыть ее простыми тестами невозможно.
В целом да. Тяжко покрыть юнит-тестами драйвер на сях/ассемблере.
Но вот в то, что вы не делаете ошибок — извините не верю. Просто вы их потом долго и нудно ловите в софтайсе, выписывая на бумажке состояния регистров и адреса переходов. А вообще, я недавно обнаружил, что даже для низкоуровневых программ, C++ в связке с CPPUnit дают довольно малое падение в быстродействии и гораздо лучшую поддерживаемость. В том числе — и за счёт покрытия тестами.
Re[10]: Ошибок не делает тот, кто ничего не делает
Здравствуйте, sc, Вы писали:
sc>Здравствуйте, bkat, Вы писали:
B>>Здравствуйте, degor, Вы писали:
B>>>>Внедрение юнит-тестов — это не более чем, B>>>>внесение порядка и предсказуемости в такую деятельность. D>>>и что, организован процесс автоматического тестирования?
B>>Ну да. Это уже дело техники, включить прогон тестов в Build. B>>Каждую ночь гоняются все тесты и к утру все кому надо получают письма с логами. sc>причем логи должны писаться и самими модулями, а не только тестами. sc>т.е. проект должен иметь достаточно мощную и удобную подсистему логирования.
Ну да...
Одно другому не мешает.
Лог от юнит-теста мне сообщает только прошел ли тест или нет,
а другой лог (от самого модуля) дает информацию для анализа почему тест не прошел.
Если все из второго лога сразу понятно, то замечательно.
Иначе в дебагер...
Здравствуйте, nikov, Вы писали:
N>Написание исчерпывающих unit-test'ов (ни одно исправление или улучшение кода не должно происходить без добавления соответствующего теста). Их регулярный автоматический запуск и автоматический контроль покрытости кода.
Это не всегда возможно. Точнее, это возможно для ограниченного круга задач.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, FDSC, Вы писали:
FDS>Здравствуйте, sc, Вы писали:
sc>>причем логи должны писаться и самими модулями, а не только тестами. sc>>т.е. проект должен иметь достаточно мощную и удобную подсистему логирования.
FDS>А что понимается под "удобной" системой логирования? FDS>Я имею ввиду, какие функции должны быть у этой системы, что бы она была удобной?
Да, удобная, понятие растяжимое
Ну хотя бы чтобы логи были достаточно детальными (хотя это и от программиста сильно зависит), анализ логов должен быть удобным (возможно отдельная тулза), уровень логирования должен настраиваться при запуске, чтобы логи не много ресурсов отъедали, ну и чтобы легко и компактно можно было вставлять логирование в код.
Кроме того, что программист пишет в лог руками, необходимо добавить автомат. логи типа вход/выход в/из ф-ции, стек вызовов и др. полезную инфу
Re[3]: Ребят, я понимаю, что вам смешно, но проблема правда
Здравствуйте, FDSC, Вы писали:
FDS>Вместо того, чтобы смеяться, лучше бы подсказали, что я не так делаю... а то я уже скоро на Nemerle и Scheme начну писать лучше, чем на C# .
Чтобы сказать что ты делаешь не так надо с тобой поработать бок о бок.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: Ребят, я понимаю, что вам смешно, но проблема правда
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, FDSC, Вы писали:
FDS>>Вместо того, чтобы смеяться, лучше бы подсказали, что я не так делаю... а то я уже скоро на Nemerle и Scheme начну писать лучше, чем на C# .
VD>Чтобы сказать что ты делаешь не так надо с тобой поработать бок о бок.
Спасибо за разъяснение.
Я просто нетерпелив и , как всегда, в плохом настроении, поэтому и пишу, что C# слишком сложен, Nemerle слишком сложен. А на самом деле, спроси меня про любимый Delphi, я и там найду, что покритиковать Так что не обращайте на меня особо внимания.
Здравствуйте, FDSC, Вы писали:
FDS>Вообще, когда используешь C# и .NET постоянно используешь сразу несколько компонентов. Обязательно надо что-то куда-то создать, записать, передать интерфейс и т.п. Куча служебных действий, иногда создаётся впечатление, что вернулся в assembler. Всё время думаешь о коде, а не о программе.
А ты не мог бы привести примеры этих "служебных действий", которые были тебе не нужны на С++?
По моим впечатлениям, код на С# обычно компактнее кода на С++.
1.2.0 alpha rev. 655
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, bkat, Вы писали:
B>Но мой опыт таков, что внедрение юнит-тестов всегда оправдывало себя. B>Затраты, на самом деле, не велики, а польза есть.
О, напиши юнит-тесты к Янусу, а потом мы посмотрим, какой процент известных ошибок ты ими поймаешь. Что то мне подсказывает, что очень маленький.
Здравствуйте, AndrewVK, Вы писали:
AVK>О, напиши юнит-тесты к Янусу,
Если изначально программа не проектировалась из расчета, что там будут unit-тесты, то именно юнит тесты написать уже не получится.
AVK>а потом мы посмотрим, какой процент известных ошибок ты ими поймаешь. Что то мне подсказывает, что очень маленький.
Кстати, какой процент известных ошибок помогла поймать статическая типизация?
Здравствуйте, Andrei N.Sobchuck, Вы писали:
AVK>>О, напиши юнит-тесты к Янусу,
ANS>Если изначально программа не проектировалась из расчета, что там будут unit-тесты, то именно юнит тесты написать уже не получится.
Знакомая отмазка.
AVK>>а потом мы посмотрим, какой процент известных ошибок ты ими поймаешь. Что то мне подсказывает, что очень маленький.
ANS>Кстати, какой процент известных ошибок помогла поймать статическая типизация?
Неизвестно. Те, что поймали, их никто из тех, кто их поймал не регистрировал.
Здравствуйте, AndrewVK, Вы писали:
AVK>>>О, напиши юнит-тесты к Янусу,
ANS>>Если изначально программа не проектировалась из расчета, что там будут unit-тесты, то именно юнит тесты написать уже не получится.
AVK>Знакомая отмазка.
Это реальность жизни. С большой долей вероятности юнит-тесты нельзя написать ввиду отсутсвия самих юнитов.
Если тебе интересно, то приведи мне пример модуля из юнита, а я скажу тебе как на него написать юнит-тест.
Здравствуйте, Andrei N.Sobchuck, Вы писали:
AVK>>Знакомая отмазка.
ANS>Это реальность жизни.
Реальности жизни это тонны написанного кода. Переписывание их тебе никто не оплатит.
ANS> С большой долей вероятности юнит-тесты нельзя написать ввиду отсутсвия самих юнитов.
А что такое юниты в твоем понимании? И как определить их наличие/отсутствие?
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, bkat, Вы писали:
B>>Но мой опыт таков, что внедрение юнит-тестов всегда оправдывало себя. B>>Затраты, на самом деле, не велики, а польза есть.
AVK>О, напиши юнит-тесты к Янусу, а потом мы посмотрим, какой процент известных ошибок ты ими поймаешь. Что то мне подсказывает, что очень маленький.
К Янусу писать не буду.
За "спасибо" не работаю
Ну и потом может вы и в самом деле сделали что-то принципиально новое
в разработке продуктов, что в итоге "старые" методы тестирования у вас
либо принпиально не применимы, либо дают нулевой эффект.
Может вы вообще все формально, на основании формальных же спецификаций, верифицируете
Я этого знать не могу
Кстати, если уж говорить об ошибках,
то важно еще учитывать, о каких ошибках идет речь
и об эффективности поиска ошибок.
Одно дело потрать год на обнаружение ошибок.
А другое дело за месяц найти те же самые ошибки.
Уверен, что целенаправленный поиск багов, в том числе с помощью юнит-тестов,
делает процесс поиска багов более эффективным,
Здравствуйте, AndrewVK, Вы писали:
ANS>>Это реальность жизни. AVK>Реальности жизни это тонны написанного кода. Переписывание их тебе никто не оплатит.
А я и несобираюсь его переписывать. Но люди иногда и пишут новый код. А со старым кодом, кроме тестеров никто не поможет.
ANS>> С большой долей вероятности юнит-тесты нельзя написать ввиду отсутсвия самих юнитов.
AVK>А что такое юниты в твоем понимании? И как определить их наличие/отсутствие?
То для чего можно написать юнит-тест То есть кусок кода, который можно выполнить не инициализируя всего приложения. Где-то так.
Здравствуйте, bkat, Вы писали:
B>Ну и потом может вы и в самом деле сделали что-то принципиально новое B>в разработке продуктов, что в итоге "старые" методы тестирования у вас B>либо принпиально не применимы, либо дают нулевой эффект.
Да нет, дело не в принципиально новом у нас, а в том, что старые методы, они далеко не для всего подходят.
B>Уверен, что целенаправленный поиск багов, в том числе с помощью юнит-тестов, B>делает процесс поиска багов более эффективным,
Вопрос только в одном — покрет ли повышение эффективности поиска багов затраты на написание юнит-тестов и модификацию их при рефакторинге системы. Для меня ответ совсем не очевиден, и я бы сильно поостерегся советовать использовать юнит-тестирование без понимания специфики конкретного проекта.
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, Andrei N.Sobchuck, Вы писали:
AVK>>>Знакомая отмазка.
Чтобы приложение было можно оттестировать unit тестами, нужно проектировать близко к IoC.
ANS>>Это реальность жизни. AVK>Реальности жизни это тонны написанного кода. Переписывание их тебе никто не оплатит.
Никто и не говорит о переписывании... но вот начинать новый проект (или модуль к старому)
лучше бы с мыслью о UT в голове...
ANS>> С большой долей вероятности юнит-тесты нельзя написать ввиду отсутсвия самих юнитов. AVK>А что такое юниты в твоем понимании? И как определить их наличие/отсутствие?
Это примерно то, на что распадается приложение, которое проектируется с использованием паттерна IoC. Если приложение не проектировалось изначально под IoC, прикрутить потом IoC — себе дороже.
Re[10]: Ошибок не делает тот, кто ничего не делает
Здравствуйте, AndrewVK, Вы писали:
AVK>Вопрос только в одном — покрет ли повышение эффективности поиска багов затраты на написание юнит-тестов и модификацию их при рефакторинге системы. Для меня ответ совсем не очевиден, и я бы сильно поостерегся советовать использовать юнит-тестирование без понимания специфики конкретного проекта.
Допустим стартует новый проект. Каким он должен быть чтобы юнит тесты были недостаточно эффективными ?
Я учавствовал в проекте, в котором юнит тесты начали писать на этапе активного багфиксинга. Процент выловленных багов благодаря юнит тестам был где-то 20%. Я вполне допускаю что для другого проекта этот показатель мог быть как 60% так и 0. А вот для нового проекта мне почему-то кажется что использование юнит тестов будет эффективным по умолчанию. Может я ошибаюсь
"Если Вы отличаетесь от меня, то это ничуть мне не вредит — Вы делаете меня богаче". Экзюпери
Re[10]: Ошибок не делает тот, кто ничего не делает
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, bkat, Вы писали:
B>>Ну и потом может вы и в самом деле сделали что-то принципиально новое B>>в разработке продуктов, что в итоге "старые" методы тестирования у вас B>>либо принпиально не применимы, либо дают нулевой эффект.
AVK>Да нет, дело не в принципиально новом у нас, а в том, что старые методы, они далеко не для всего подходят.
Дак понятно, что серебрянных пуль нету.
B>>Уверен, что целенаправленный поиск багов, в том числе с помощью юнит-тестов, B>>делает процесс поиска багов более эффективным,
AVK>Вопрос только в одном — покрет ли повышение эффективности поиска багов затраты на написание юнит-тестов и модификацию их при рефакторинге системы. Для меня ответ совсем не очевиден, и я бы сильно поостерегся советовать использовать юнит-тестирование без понимания специфики конкретного проекта.
Да, для каждого проекта надо решать заново, что и как тестируется.
Это даже надо заранее планировать.
Юнит-тестирование — это один из "тулов" обеспечения качества.
О нем надо знать и при необходимости осознанно пользоваться.
Советовать этим пользоваться всегда и при любых условиях так не разумно,
на мой взгляд, как и "сильно остерегаться советовать использовать".
Членам РСДН стоит вообще быть осторожным с советами, потому что к ним прислушиваются
Re[13]: Ошибок не делает тот, кто ничего не делает
Здравствуйте, aka50, Вы писали:
AVK>>>>Знакомая отмазка. A>Чтобы приложение было можно оттестировать unit тестами, нужно проектировать близко к IoC.
Янус спроектирован таким образом. Только проблема там в другом месте.
AVK>>А что такое юниты в твоем понимании? И как определить их наличие/отсутствие? A>Это примерно то, на что распадается приложение, которое проектируется с использованием паттерна IoC. Если приложение не проектировалось изначально под IoC, прикрутить потом IoC — себе дороже.
Здравствуйте, AndrewVK, Вы писали:
AVK>Вопрос только в одном — покрет ли повышение эффективности поиска багов затраты на написание юнит-тестов и модификацию их при рефакторинге системы. Для меня ответ совсем не очевиден, и я бы сильно поостерегся советовать использовать юнит-тестирование без понимания специфики конкретного проекта.
Проведение рефакторинга системы без юнит-тестов — это bugs*bugs при котором bugs -> infinity
Re[14]: Ошибок не делает тот, кто ничего не делает
Здравствуйте, Mirrorer, Вы писали:
M>Я учавствовал в проекте, в котором юнит тесты начали писать на этапе активного багфиксинга. Процент выловленных багов благодаря юнит тестам был где-то 20%. Я вполне допускаю что для другого проекта этот показатель мог быть как 60% так и 0. А вот для нового проекта мне почему-то кажется что использование юнит тестов будет эффективным по умолчанию. Может я ошибаюсь
Ну а у меня есть один относительно небольшой проект — библиотечка. Я его специально изначально писал с хорошим покрытием юнит-тестами. В результате сил было на них убито масса (особенно доставало то, что после масштабного рефакторинга ломалась сразу куча тестов, к самому рефакторингу прямого отношенияы не имеющая). При этом эти самые юнит-тесты поймали 1 (одну) ошибку.
Здравствуйте, Andrei N.Sobchuck, Вы писали:
ANS>Здравствуйте, AndrewVK, Вы писали:
AVK>>Янус спроектирован таким образом. Только проблема там в другом месте.
ANS>О да. Просвети нас, чем же именно уникален Янус?
Думаю, дело не в уникальности, а в стоимости бага. Для Януса пропущенный баг стоит очень недорого, и, возможно, действительно нет смысла городить систему модульного тестирования, так как она может оказаться дороже суммарной стоимости пропущенных багов. Но есть и другие приложения, в которых стоимость бага очень высокая. Вот там без модульного тестирования выжить весьма сложно.
лэт ми спик фром май харт
Re[12]: Ошибок не делает тот, кто ничего не делает
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, Mirrorer, Вы писали:
M>>Я учавствовал в проекте, в котором юнит тесты начали писать на этапе активного багфиксинга. Процент выловленных багов благодаря юнит тестам был где-то 20%. Я вполне допускаю что для другого проекта этот показатель мог быть как 60% так и 0. А вот для нового проекта мне почему-то кажется что использование юнит тестов будет эффективным по умолчанию. Может я ошибаюсь
AVK>Ну а у меня есть один относительно небольшой проект — библиотечка. Я его специально изначально писал с хорошим покрытием юнит-тестами. В результате сил было на них убито масса (особенно доставало то, что после масштабного рефакторинга ломалась сразу куча тестов, к самому рефакторингу прямого отношенияы не имеющая). При этом эти самые юнит-тесты поймали 1 (одну) ошибку.
А если бы ты со своей библиотечкой решил присоединиться к многочисленно когорте программистов-ядерщиков, и из-за этого бага взорвался бы ядерный реактор?
лэт ми спик фром май харт
Re[12]: Ошибок не делает тот, кто ничего не делает
Здравствуйте, prVovik, Вы писали:
V>Здравствуйте, red_dragon, Вы писали:
_>>Проведение рефакторинга системы без юнит-тестов — это bugs*bugs при котором bugs -> infinity
V>Несмотря на то, что я за юнит тесты, но данное высказывание более чем спорно
Делали рефакторинг без юнит-тестов?
Я делал В итоге что-бы все заработало как надо, потратил массу времени, ну и чистота кода оставляет желать лучшего
Re[16]: Ошибок не делает тот, кто ничего не делает
Здравствуйте, red_dragon, Вы писали:
_>Делали рефакторинг без юнит-тестов?
_>Я делал В итоге что-бы все заработало как надо, потратил массу времени, ну и чистота кода оставляет желать лучшего
это ни о чем не говорит. разве что о вашей квалификации.
Re[13]: Ошибок не делает тот, кто ничего не делает
Здравствуйте, red_dragon, Вы писали:
_>Делали рефакторинг без юнит-тестов?
_>Я делал В итоге что-бы все заработало как надо, потратил массу времени, ну и чистота кода оставляет желать лучшего
Просто если язык статически типизирован, рефлексия используется к месту, и код изначально написан с соблюдением примитивных правил приличия, то рефакторинг без юнит тестов не так страшен. Хотя, если рефакторинг затрагивает изменение структуры БД, то тут без юнит тестов действительно начинается попа
лэт ми спик фром май харт
Re[14]: Ошибок не делает тот, кто ничего не делает
Здравствуйте, degor, Вы писали:
D>Здравствуйте, red_dragon, Вы писали:
_>>Делали рефакторинг без юнит-тестов?
_>>Я делал В итоге что-бы все заработало как надо, потратил массу времени, ну и чистота кода оставляет желать лучшего
D>это ни о чем не говорит. разве что о вашей квалификации.
я вам улыбаюсь
Re[17]: Ошибок не делает тот, кто ничего не делает
Здравствуйте, prVovik, Вы писали:
V>А если бы ты со своей библиотечкой решил присоединиться к многочисленно когорте программистов-ядерщиков, и из-за этого бага взорвался бы ядерный реактор?
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, Mirrorer, Вы писали:
M>>Я учавствовал в проекте, в котором юнит тесты начали писать на этапе активного багфиксинга. Процент выловленных багов благодаря юнит тестам был где-то 20%. Я вполне допускаю что для другого проекта этот показатель мог быть как 60% так и 0. А вот для нового проекта мне почему-то кажется что использование юнит тестов будет эффективным по умолчанию. Может я ошибаюсь
AVK>Ну а у меня есть один относительно небольшой проект — библиотечка. Я его специально изначально писал с хорошим покрытием юнит-тестами. В результате сил было на них убито масса (особенно доставало то, что после масштабного рефакторинга ломалась сразу куча тестов, к самому рефакторингу прямого отношенияы не имеющая). При этом эти самые юнит-тесты поймали 1 (одну) ошибку.
Кстати, а почему возникла необходимость в масштабном рефакторинге?
Может сначала стоит потратить время на то, чтобы продумать интерфейсы.
Если интерфейсы потом сильно меняются, то это всегда очень дорого.
Менять приходится все, что зависит от измененных интерфейсов.
Всю документацию, и всех, кто интерфейсы пользует.
Юнит-тесты тут совсем не причем. Юнит-тесты — это всего лишь один из многих,
кто пользуется интерфейсами твоей библиотеки.
Re[18]: Ошибок не делает тот, кто ничего не делает
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, Andrei N.Sobchuck, Вы писали:
AVK>>>А кто сказал, что он уникален? ANS>>Хорошо. Тогда скажи в каком именно другом месте проблема. AVK>Место называется GUI
И что в нем такого уникального? Существуют системы тестирования гуев, очень неплохо
себя оправдывают, например в одном проекте на netbeans использовался http://xtest.netbeans.org/,
по багтраку примерно 30% ошибок гуевых им выловлено.
Re[19]: Ошибок не делает тот, кто ничего не делает
Здравствуйте, Andrei N.Sobchuck, Вы писали:
AVK>>Место называется GUI
ANS>Согласен, если приложение состоит только из гуя и совершенно не содержит логики, то тут юнит тесты не помогут.
Не, ну Янус конечно кое какую логику содержит. Но, вот беда, львиная доля багов в нем приходится как раз на GUI.
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, aka50, Вы писали:
AVK>>>А что такое юниты в твоем понимании? И как определить их наличие/отсутствие? A>>Это примерно то, на что распадается приложение, которое проектируется с использованием паттерна IoC. Если приложение не проектировалось изначально под IoC, прикрутить потом IoC — себе дороже.
AVK>Да, ребяты. Вопросов больше не имею.
А расшифровать? А то ощущение что "послали"...
Re[19]: Ошибок не делает тот, кто ничего не делает
Здравствуйте, AndrewVK, Вы писали:
AVK>Не, ну Янус конечно кое какую логику содержит. Но, вот беда, львиная доля багов в нем приходится как раз на GUI.
Ладно. Что бы поспорить и что-то доказать прийдётся сырцы Януса читать. Я уж лучше просто останусь при мнении, что юнит-тесты вельми полезная штука
Здравствуйте, AndrewVK, Вы писали:
B>>Может сначала стоит потратить время на то, чтобы продумать интерфейсы.
AVK>Как все просто
Чертовски сложно, но никуда без этого.
Иначе ведь вообще ничего до конца не довести.
Ну в общем у нас явно разный опыт.
У меня затраты на юнит-тесты не настолько велики, чтобы от них (юнит-тесты) стоило бы отказаться.
При всяких рефакторингах затраты на обновление именно юнит-тестов минимальны.
Потом, как я уже писал в другом топике, все равно программер
практически всегда создает свое окружение, в котором он может "играться"
со своим кодом и как-то убеждаться в том, что его творение в принципе дышит
и делает ожидаемые вещи.
Разработку без подобного окружения я лично вообще не представляют.
Юнит-тесты — это просто часть этого окружения.
Можно вообще большую часть подобного окружения поддерживать именно как юнит-тесты.
Re[14]: Ошибок не делает тот, кто ничего не делает
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, red_dragon, Вы писали:
_>>Делали рефакторинг без юнит-тестов?
AVK>Я делаю регулярно. Ужасов не замечено.
Каким образом уверяешься, что ничего не поломал при рефакторинге? Кент Бек в Test Driven Development говорит о юнит-тестах как раз о средстве повишения уверенности.
Еще тут было замечание что при рефакторинге ломается много тестов — ну вы для того же их и писали, чтобы зафиксировать поведение системы и быть уверенными в том, что она ведет себя именно так, как до правок. Если ломается слишком много тестов — значит у вас одно и то же действие тестируется многими тестами.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[15]: Ошибок не делает тот, кто ничего не делает
Здравствуйте, Gajdalager, Вы писали:
G>Каким образом уверяешься, что ничего не поломал при рефакторинге? Кент Бек в Test Driven Development говорит о юнит-тестах как раз о средстве повишения уверенности.
это для слабых духом. настоящий программист всегда уверен в себе.
Re[15]: Ошибок не делает тот, кто ничего не делает
Здравствуйте, aka50, Вы писали:
A>>>Это примерно то, на что распадается приложение, которое проектируется с использованием паттерна IoC. Если приложение не проектировалось изначально под IoC, прикрутить потом IoC — себе дороже.
AVK>>Да, ребяты. Вопросов больше не имею. A>А расшифровать? А то ощущение что "послали"...
У меня как то в нематерный русский не расшифровывается, уж извини.
Здравствуйте, bkat, Вы писали:
B>>>Может сначала стоит потратить время на то, чтобы продумать интерфейсы.
AVK>>Как все просто
B>Чертовски сложно, но никуда без этого. B>Иначе ведь вообще ничего до конца не довести.
Уверен? А как же XP?
B>Потом, как я уже писал в другом топике, все равно программер B>практически всегда создает свое окружение, в котором он может "играться" B>со своим кодом и как-то убеждаться в том, что его творение в принципе дышит B>и делает ожидаемые вещи.
Видишь ли, простенькое окружение и хорошее покрытие юнит-тестов это две большие разницы. Ничего не имею против нескольких тестов, имитирующих базовые сценарии использования тестируемого кода.
Здравствуйте, Gajdalager, Вы писали:
G>Каким образом уверяешься, что ничего не поломал при рефакторинге? Кент Бек в Test Driven Development говорит о юнит-тестах как раз о средстве повишения уверенности.
Мне за уверенность не платят
А если серьезно, то тут уже ответили. Статическая типизация + грамотный дизайн позволяют минимизировать неотловленные побочные эффекты.
G>Еще тут было замечание что при рефакторинге ломается много тестов — ну вы для того же их и писали, чтобы зафиксировать поведение системы и быть уверенными в том, что она ведет себя именно так, как до правок.
Э нет. Не все так просто. Правильное их срабатывание, это когда они срабатывают там, где я этого не ожидал. А если у меня юнит-тесты ломаются не из-за того, что сломалось то, что они непосредственно проверяли, а просто потому что система сильно изменилась, то это срабатывание бесполезно.
Несколько практических советов от настоящих индейцев.
1. Настоящий индеец прежде всего заходит в меню Debug и в диалоге Exceptions включает галку Thrown на CLR Exceptions для managed языков. Это позволяет сэкономить не просто хучу, а туеву хучу времени при поиске ошибок. Отсюда следствие — настоящие индейцы не используют логику на исключениях, иначе весь кайф пропадает.
2. В простом и понятном коде сложно сделать ошибку, а сделав легко найти. Следовательно, написанный код должен быть понятен с первого взгляда. Настоящие индейцы производят немногословный и хорошо отформатированный код. После написания и отладки готовый код ещё раз просмотриваем на предмет как его можно сделать более лаконичным и понятным. Настоящие индейцы пытаются сделать код более понятным прежде всего не для себя, а для других, даже если другими этот код никогда просматриваться не будет.
3. Copy/Paste vs. повторное использование. Copy/Paste — это разносчик багов, что есть плохо. Но шизиловка, когда каждые две строчки кода оформляются в виде отдельного метода ни чем не лучше. Поэтому настоящие индейцы копипейстят, но только один раз. Если некоторый фрагмент кода повторяется уже в третий раз, то это хорошая причина для оформления его в виде отдельного метода.
4. Настоящие индейцы не боятся длинных линейных методов, они не любят запутанных ветвистых уродцев, в которых легко прятаться багам. Существуют задачи, например, по генерации кода, которые выполняются в несколько последовательных шагов. Разбиение таких задач на несколько методов, которые используются только один раз не имеет никакого смысла, особенно учитывая, что часто приходится передавать контекст в эти методы, что выливается в длиннючий список параметров. Лучше отделить более менее независимые блоки коментариями, кратко описывающими суть действия. Если же выполняемая операция становится действительно сложной и плохоуправляемой, а рефакторинг такого метода приводит к выделению методов с невменяемым числом передаваемых параметров, то настоящие индейцы выносят такой код в отдельный класс. В отличии от метода в отдельном классе вместо передаваемых параметров можно использовать контекст самого класса.
5. Настоящий индеец думает 10 раз прежде чем объявить статическую переменную. Статический контекст самый злобный источник багов и кривого дизайна. Даже если не будет багов, то кривизну дизайна в дальнейшем придётся выкривлять с помощью линз с обратной кривизной. Если же индеец всё же решилися на использование статического контекста, то он возвращается к началу данного пункта ещё раз. В принципе, статические переменные можно использовать для кеширования некоторых данных при соблюдении правил гигиены в многопоточных приложениях. В остальных случаях как правило стоит серьёзно задуматься.
6. Автоматическое тестирование. Настоящие индейцы обязательно пишут тесты для тестирования библиотечного кода, который используется другими частями приложения. Прикладной код тоже иногда может автоматически тестироваться, но это может оказаться банально дорого, т.к. тестирование библиотечного кода и например UI — это принципиально разные вещи. Юнит тест для библиотеки не стоит практически ничего, подготовка и периодическое изменение теста для UI может занять больше времени, чем работа над самим UI.
7. Ошибки происходят не только из-за неправильного кода, но и из-за неправильных данных, которые впрочем могут быть порождены неправильным кодом. Настоящие индейцы используют логи и вывод отладочной печати в отладочное окно студии. Зачастую сложную структуру данных проще напечатать и иметь сразу всю картину, чем бродить по окну Watch, заглядывая по очереди во все переменные класса.
8. Настоящий индеец знает, что только что написанный код обязательно будет меняться либо им самим, либо кем-то другим, может быть через 5 минут, может быть через 5 лет. В трудно модифицируемом коде сложно исправлять существующие баги и не вностить новые. Поэтому, основной критерий качества кода для настоящего индейца — это готовность кода к модификациям и способность после этого оставаться готовым к следующим модификациям.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, Zuka, Вы писали:
Z>Здравствуйте, Tilir, Вы писали:
T>>В целом да. Тяжко покрыть юнит-тестами драйвер на сях/ассемблере.
Z>А что тяжкого-то? Берем и паяем MockDevice...
An emulator contains both hardware and software technology. Emulation hardware consists of functionality on the DSP chip, which enables the collection of data. This data provides state behavior and other system visibility. Hardware is also required to extract this information from the DSP device at high rates and format the data. Emulator software provides additional higher level control and an interface with the host computer, usually in terms of an interface called a debugger. The debugger provides the development engineer an easy migration from the compilation process (compiling, assembling, and linking an application) to the execution environment. The debugger takes the output from the compilation process (for example, a .out file) and loads the image into the target system. The engineer then uses the debugger to interact with the emulator to control and execute the application and find and fix problems. These problems can be hardware as well as software problems. The emulator is designed to be a complete integration and test environment
Re[16]: Ошибок не делает тот, кто ничего не делает
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, bkat, Вы писали:
B>>>>Может сначала стоит потратить время на то, чтобы продумать интерфейсы.
AVK>>>Как все просто
B>>Чертовски сложно, но никуда без этого. B>>Иначе ведь вообще ничего до конца не довести.
AVK>Уверен? А как же XP?
А что XP? Про него ничего не знаю
А вот если ты разрабатываешь библиотеку для других и
будешь постоянно менять интерфейсы, то успеха точно не будет.
Здравствуйте, Sinclair, Вы писали:
S>Здравствуйте, FDSC, Вы писали:
FDS>>Вообще, когда используешь C# и .NET постоянно используешь сразу несколько компонентов. Обязательно надо что-то куда-то создать, записать, передать интерфейс и т.п. Куча служебных действий, иногда создаётся впечатление, что вернулся в assembler. Всё время думаешь о коде, а не о программе. S>А ты не мог бы привести примеры этих "служебных действий", которые были тебе не нужны на С++? S>По моим впечатлениям, код на С# обычно компактнее кода на С++.
Дело в том, что если бы я писал функцию сохранения в файл на C++ я бы написал всё с нуля, но вызов был бы в одну строку.
А, скажем, для сериализации мне необходимо (вместо пары сотен строк)
1. Создать поток, связанный с файлом, открытым в правильном режиме (одна строка)
2. Создать форматтер (одна строка)
3. Передать в форматтер сериализуемый объект.
Вот я и говорю: я просто нетерпелив. Мне мешает то, что это занимает 3 строки
При сравнении строк примерно то же: я должен создать итератор, что бы обращаться к строке независимо от кодировки, затем я должен выбрать каким образом я должен сравнивать строки (т.е. настройки локализации) и т.д.
Само по себе это всё идёт плюс несколько строк в код, но экономит, за счёт того, что это сделано, очень много кода. Но то, что я должен думать о том, что у меня в using не стоит System.IO при выполнении сериализации, которая якобы реализована, лично меня очень сильно раздражает. Именно тем, что думаешь уже высокоуровневыми терминами и когда тебе компилятор выдаёт ошибку, что он не нашёл соотв. namespace System.IO, ты как-будто об стенку грохаешься. Т.е. я бы ожидал отдельно для бинарной сериализации некоторый конструктор форматтера, который бы брал все необходимые параметры и не заставлял создавать мне поток, который я, скорее всего, после сериализации сразу же закрою.
Соотв., пример кода можно взять даже из rsdn.editor или любой другой программы: я специально смотрел код других программистов. Насколько я помню, там Влад (и кто там ещё?) всё делал как раз в таком же духе...
Re[17]: Ошибок не делает тот, кто ничего не делает
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, bkat, Вы писали:
B>>А вот если ты разрабатываешь библиотеку для других и B>>будешь постоянно менять интерфейсы, то успеха точно не будет.
AVK>Вот если я ее уже кому то отдал, то да. А если я ее только разрабатываю?
Разрабатываешь все равно с целью получить стабильные интерфесы,
которые не придется постоянно перелапачивать.
В общем это абстрактный спор, который совершенно неконструктивен.
Повторюсь, юнит-тесты — это один из способов, который в ряде случаев работает.
Но это конечно не серебренная пуля.
Верю, что в твоем конкретном случае юнит-тесты не работают,
но это не повод мне, и другим, от него отказываться.
Впрочем и мой положительый опыт ты тоже можешь запросто игнорировать,
если ты достигаешь качества другими, более эффективными способами.
Ничего против этого не имею
Re[18]: Ошибок не делает тот, кто ничего не делает
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, bkat, Вы писали:
B>>А вот если ты разрабатываешь библиотеку для других и B>>будешь постоянно менять интерфейсы, то успеха точно не будет.
AVK>Вот если я ее уже кому то отдал, то да. А если я ее только разрабатываю?
Здравствуйте, bkat, Вы писали:
AVK>>Вот если я ее уже кому то отдал, то да. А если я ее только разрабатываю?
B>Разрабатываешь все равно с целью получить стабильные интерфесы, B>которые не придется постоянно перелапачивать.
Ну то есть тесты надо писать в конце, когда уже все интерфейсы стабилизировались?
B>В общем это абстрактный спор
А по мне так совершенно конкретный.
B>Верю, что в твоем конкретном случае юнит-тесты не работают, B>но это не повод мне, и другим, от него отказываться.
А я и не призываю никого отказываться. Я призываю голову включать, а не агитировать за совецкую власть.
B>Хотя IT тоже упомянул про автоматическое unit тестирование...
У IT немного иное понимание unit-тестирования. В свое время мы, кажется здесь, это уже обсуждали и пришли к консенсусу. Суть в том, что не нужно добиваться полного покрытия, нужно описывать в тестах только основные сценарии применения. Против этого (я, кажется, сегодня уже писал) я ничего не имею. Но это не совсем unit-тестирование в классическом его понимании.
Здравствуйте, FDSC, Вы писали:
FDS>Само по себе это всё идёт плюс несколько строк в код, но экономит, за счёт того, что это сделано, очень много кода. Но то, что я должен думать о том, что у меня в using не стоит System.IO при выполнении сериализации, которая якобы реализована, лично меня очень сильно раздражает. Именно тем, что думаешь уже высокоуровневыми терминами и когда тебе компилятор выдаёт ошибку, что он не нашёл соотв. namespace System.IO, ты как-будто об стенку грохаешься.
У тебя 2005 студия? На смарттеги внимания не обращаешь? Тогда поставь Решарпер.
Здравствуйте, IT, Вы писали:
IT>Несколько практических советов от настоящих индейцев.
IT>7. Ошибки происходят не только из-за неправильного кода, но и из-за неправильных данных, которые впрочем могут быть порождены неправильным кодом. Настоящие индейцы используют логи и вывод отладочной печати в отладочное окно студии. Зачастую сложную структуру данных проще напечатать и иметь сразу всю картину, чем бродить по окну Watch, заглядывая по очереди во все переменные класса.
Ммм. А если их сложно напечатать, что делать?
Например, 4-х мерная матрица 100х100х100х100 (помню, я так и не написал ту программу из-за того, что не смог нормально проанализировать её содержание)
Или, скажем, граф с представлением в виде списков следования? Это ж замучиться распечатывать! (я форму специальную делал)
IT>3. Copy/Paste vs. повторное использование. Copy/Paste — это разносчик багов, что есть плохо. Но шизиловка, когда каждые две строчки кода оформляются в виде отдельного метода ни чем не лучше. Поэтому настоящие индейцы копипейстят, но только один раз. Если некоторый фрагмент кода повторяется уже в третий раз, то это хорошая причина для оформления его в виде отдельного метода.
Хм. А вот что делать, если мне нужно реализовать процедуру умножения транспонированных матриц (очень просто, неправда ли?).
Варианты реализации:
1. Для того, что бы перемножить некоторым образом транспонированные матрицы я их транспонирую отдельной функцией и затем передаю в обычную процедуру умножения матриц
Минус: понижение производительности. Ведь я мог бы перемножить матрицы без физического транспонирования
2. Записать почти одинаковые алгоритмы для всех 4-х вариантов (т.е. AB, AtB, ABt, AtBt)
Минус: 4 раза копируется один и тот же метод
3. Записать метод с передачей в него флагов транспонированности, но тогда там тоже будет небольшой copy/paste
Минус: пользователь запутывается с флагами и становится тяжело использовать метод или необходимо написать 4 доп. метода, вызывающих этот для разных вариантов перемножений
4. Обращаться к матрице через свойство, что бы можно было не транспонировать матрицу физически
Минусы: * придётся возится с передачей указателей на соотв. методы взятия элемента матрицы, что неудобно
* небольшое (но чувствительное) снижение производительности в связи с дополнительными вызовами методов
5. Я могу писать перемножение прямо в методе (кстати, так и делаю, хоть дальше ругаюсь на пункт 4)
Минус: постоянные повторения одного и того же кода
Какой из этих вариантов предпочтительней?
И не будет ли после того, как написал два раза одинаковый код, мучительно вспоминать, а где же я его ещё писал-то?
IT>4. Настоящие индейцы не боятся длинных линейных методов, они не любят запутанных ветвистых уродцев, в которых легко прятаться багам.
Вот с этого начнём, точнее закончим. Не согласен
Где прятаться багам, если методы до отупения просты? Представьте себе, у вас все методы из 3-5 строк. Ну 10 максимум, обычно.
При этом есть одно условие: название вызываемого метода говорит о том, что этот метод делает, а этот метод делает только то, что сказано в названии (это, кстати, рекомендация Макконнелла). Тогда получаем кучу вот этих самых методов, которые просто и в которых не где прятаться багам, так как, фактически, такой код представляет собой обычный псевдокод.
Я почему это говорю, я не так давно пробовал (на C++) писать исключительно мелкими методами, как сказал, и скорость разработки у меня повысилась приблизительно в 1,5 раза (с отладкой). При этом большое количество багов из разряда логических ошибок перекочевало в разряд "забыл вызвать метод", "при кодировании использовал неправильный алгоритм расчётов". Кстати, в последнем случае, в мелких методах гораздо лучше видно несоответствие кода используемому алгоритму и несоответствие алгоритма решаемой задачи.
IT>Существуют задачи, например, по генерации кода, которые выполняются в несколько последовательных шагов. Разбиение таких задач на несколько методов, которые используются только один раз не имеет никакого смысла, особенно учитывая, что часто приходится передавать контекст в эти методы, что выливается в длиннючий список параметров. Лучше отделить более менее независимые блоки коментариями, кратко описывающими суть действия.
О! В январе так делал. Правда на Delphi, а не на C#. Сначала я написал бо-о-о-ольшую процедуру (200-300 строк). Её предназначение было — распознавание зашумлённых шестнадцатиричных чисел, написанных от руки на неровной поверхности. Дальше у меня появились предложения по улучшению... я стал писать вложенные процедуры, по 50-70 строк. Дальше это всё отказалось стабильно и хорошо работать . В итоге вся эта первоначальная процедура с вложенными функциями занимала почти 1000 строк.
IT> Если же выполняемая операция становится действительно сложной и плохоуправляемой, а рефакторинг такого метода приводит к выделению методов с невменяемым числом передаваемых параметров, то настоящие индейцы выносят такой код в отдельный класс. В отличии от метода в отдельном классе вместо передаваемых параметров можно использовать контекст самого класса.
Число параметров у методов было довольно небольшим, но их было ОЧЕНЬ много, до того много, что я стал их просто терять, я стал не понимать вообще где используется метод, который я вижу.
Я переписал всё через несколько классов, используя маленькие логичные как можно более простые блоки (и как можно более простые классы). Заняло у меня это один день, всё заработало и даже довольно прилично смотрится.
Как вы это объясните?
Кстати, таких примеров из моей личной практики программирования до фига. Я имею ввиду, преимущества мельких методов над крупными, но с комментариями над каждой частью. Например, год назад я писал рассчёт обратной задачи кинематики плоского манипулятора с замкнутой кинематикой (на C#) и заметил ошибку в реализации метода Ньютона только когда разбил его на много мелких методов. До этого же эта ошибка существовала чуть ли не с самого начала, я о ней знал, но думал совершенно на другие части программы.
Осенью я писал расчёт деформаций шпинделя (C++) и пока не начал разбивать неожиданно разросшийся класс на два, не нашёл правильного решения. Плюс, у меня там был как раз такой метод, какой вы советуете: генерация матрицы жёсткости. В этом методе было наибольшее число ошибок, он был труден для понимания и т.п. Разбей я его на несколько методов — всё было бы хорошо.
Может быть я вас неправильно понял?
Вот этот уродец (он и сейчас такой ):
// Вычисление формы деформации конструкции и реакций опорvoid Spindle::CalcShifts()
{
// Вычислить длины массива параметров опор и количество столбцов в матрице жёсткостиint SC = Iteration + 2;
int Lk = (SC + 1) * 2;
K = new double[Lk * (Lk + 1)];
double * Kf = new double[Lk * (Lk + 1)];
for (int i = 0; i < Lk * (Lk + 1); K[i++] = 0.0);
// Создать систему разрешающих уравнений
// Записать коэффициенты уравнений без учёта кинематических ограниченийfor (i = 0; i < SC; i++)
{
Bars[i]->SetK(K, Lk);
}
// Записать правые части
*(K + 0 + Lk) = F;
*(K + (Lk + 1) + Lk) = M;
// Скопировать матрицу жесткостей без кинематических ограничений для дальнейших вычислений рекаций опорfor (i = 0; i < Lk * (Lk + 1); i++)
{
Kf[i] = K[i];
}
// Учесть кинематические ограничения первых двух опор обнулением столбцов, так как это упростит расчётfor (i = 0; i < Lk; i++)
{
*(K + (Lk + 1) * i + (2) ) = 0.0;
*(K + (Lk + 1) * i + (Lk - 2)) = 0.0;
}
// Учесть кинематические ограничения всех опорfor (int j = 0; j < SC; j++)
{
for(int i = 0; i < Lk; i++)
{
*(K + (Lk + 1) * (j + 1)*2 + i) = 0.0; // j + 1, так как первый узел ни на что не опирается; *2 - так как узлу соответсвует две строчки (перемещение и поворот)
}
*(K + (Lk + 1) * (j + 1)*2 + Lk) = Sp[j + this->maxSupportCount * 2]; // Координата кинематического ограничения по y
*(K + (Lk + 1) * (j + 1)*2 + (j + 1)*2) = 1.0; // Единица на главной диагонали
// ::MessageBox(0, itoa(Sp[j + this->maxSupportCount * 2]*1000000000, new char[11], 10), "EE", 0);
}
// Решить системуdouble * Y = NULL;
// Отладка
// #define DBG__#ifdef DBG__
static int Z = 0;
Z++;
HANDLE File;
int brum = 0;
if (Z == 2)
{
File = ::CreateFileA("G:\\debug.tmp", GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
::WriteFile(File, K, Lk*(Lk + 1)*sizeof(double), (LPDWORD)&brum, 0);
}
#endif
MGauss(K, Y, Lk);
// Отладка#ifdef DBG__
if (Z == 2)
{
::WriteFile(File, Y, Lk*sizeof(double), (LPDWORD)&brum, 0);
::CloseHandle(File);
}
#endif
#undef DBG__
// Установить эти значения в стержниfor (i = 0; i < SC; i++)
{
Bars[i]->SetY(Y);
}
// Вычислить реакции опор (только силы, моменты равны нулю)for (i = 0, j = 2; j < Lk; i++, j += 2)
{
// Обнуление обязательно, т.к. на каждой итерации расчёта идёт запись в одну и ту же строкуthis->Sp[maxSupportCount*3 + i] = 0.0;
for (int k = 0; k < Lk; k++)
{
this->Sp[maxSupportCount*3 + i] += Y[k] * Kf[k + (Lk + 1)*j];
}
}
delete [] Kf;
delete [] K;
delete [] Y;
}
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, FDSC, Вы писали:
FDS>>Само по себе это всё идёт плюс несколько строк в код, но экономит, за счёт того, что это сделано, очень много кода. Но то, что я должен думать о том, что у меня в using не стоит System.IO при выполнении сериализации, которая якобы реализована, лично меня очень сильно раздражает. Именно тем, что думаешь уже высокоуровневыми терминами и когда тебе компилятор выдаёт ошибку, что он не нашёл соотв. namespace System.IO, ты как-будто об стенку грохаешься.
AVK>У тебя 2005 студия? На смарттеги внимания не обращаешь? Тогда поставь Решарпер.
Сейчас 2005. Что такое смартеги даже не знаю Ээээ, может кто-нибудь подскажет?
Здравствуйте, aka50, Вы писали:
A>Здравствуйте, Zuka, Вы писали:
Z>>Здравствуйте, Tilir, Вы писали:
T>>>В целом да. Тяжко покрыть юнит-тестами драйвер на сях/ассемблере.
Z>>А что тяжкого-то? Берем и паяем MockDevice...
A>Ты будешь смеяться, но так обычно и делают... http://www.ddj.com/dept/debug/197801325
Угу, особенно хорошо получается, когда не уверен в том, что документация по контроллеру правильная... точнее уверен, что в ней ошибка.
Тогда уже вообще не понятно, что тестируешь: устройство или драйвер для него.
Re[20]: Ошибок не делает тот, кто ничего не делает
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, bkat, Вы писали:
AVK>>>Вот если я ее уже кому то отдал, то да. А если я ее только разрабатываю?
B>>Разрабатываешь все равно с целью получить стабильные интерфесы, B>>которые не придется постоянно перелапачивать.
AVK>Ну то есть тесты надо писать в конце, когда уже все интерфейсы стабилизировались?
Например так.
Порядок (тесты->код или наоборот) для меня особой роли не играет.
Важно, чтобы в итоге тесты были.
Когда я отдаю стабильную версию на сторону (может быть коллега-сосед),
предпочитаю чтобы тесты были. Но в принципе не принципиально.
B>>В общем это абстрактный спор
AVK>А по мне так совершенно конкретный.
Конкретным он был бы, если бы вместе работали над одним проектом.
А без конкретики, нужны ли юнит-тесты или не нужны — спор не очень конструктивен.
Иначе я не могу понять, почему это интерфейсы нельзя зафиксировать настолько,
чтобы можно было задуматься о юнит тестах, а для тебя будет смешной мысль о том,
что интерфесы надо бы продумать и по возможности прийти к стабильному варианту.
B>>Верю, что в твоем конкретном случае юнит-тесты не работают, B>>но это не повод мне, и другим, от него отказываться.
AVK>А я и не призываю никого отказываться. Я призываю голову включать, а не агитировать за совецкую власть.
Забавно, что и к тому же самому призываю
IT>>4. Настоящие индейцы не боятся длинных линейных методов, они не любят запутанных ветвистых уродцев, в которых легко прятаться багам.
FDS>Вот с этого начнём, точнее закончим. Не согласен
FDS>Где прятаться багам, если методы до отупения просты? Представьте себе, у вас все методы из 3-5 строк. Ну 10 максимум, обычно.
У тебя никогда не было такого, что каждый отдельный метод на 3-5 строк делает
вроде все правильно, а вот в совокупности получается фигня?
Здравствуйте, FDSC, Вы писали:
FDS>Угу, особенно хорошо получается, когда не уверен в том, что документация по контроллеру правильная... точнее уверен, что в ней ошибка. FDS>Тогда уже вообще не понятно, что тестируешь: устройство или драйвер для него.
Ну от этого никто не застрахован, бывает и тесты ошибки содержут
Здравствуйте, FDSC, Вы писали:
FDS>Ммм. А если их сложно напечатать, что делать? FDS>Например, 4-х мерная матрица 100х100х100х100 (помню, я так и не написал ту программу из-за того, что не смог нормально проанализировать её содержание) FDS>Или, скажем, граф с представлением в виде списков следования? Это ж замучиться распечатывать! (я форму специальную делал)
А что мешает в этом случае делать дамп в файл? Тесты даже могут сравнивать его с заранее правильным файлом.
IT>>3. Copy/Paste vs. повторное использование. Copy/Paste — это разносчик багов, что есть плохо. Но шизиловка, когда каждые две строчки кода оформляются в виде отдельного метода ни чем не лучше. Поэтому настоящие индейцы копипейстят, но только один раз. Если некоторый фрагмент кода повторяется уже в третий раз, то это хорошая причина для оформления его в виде отдельного метода.
FDS>Хм. А вот что делать, если мне нужно реализовать процедуру умножения транспонированных матриц (очень просто, неправда ли?). FDS>Варианты реализации:
[skip]
FDS>Какой из этих вариантов предпочтительней? FDS>И не будет ли после того, как написал два раза одинаковый код, мучительно вспоминать, а где же я его ещё писал-то?
inline спасет... я так думаю в купе с шаблонами, если это С++.
FDS>О! В январе так делал. Правда на Delphi, а не на C#. Сначала я написал бо-о-о-ольшую процедуру (200-300 строк). Её предназначение было — распознавание зашумлённых шестнадцатиричных чисел, написанных от руки на неровной поверхности. Дальше у меня появились предложения по улучшению... я стал писать вложенные процедуры, по 50-70 строк. Дальше это всё отказалось стабильно и хорошо работать . В итоге вся эта первоначальная процедура с вложенными функциями занимала почти 1000 строк.
Рефакторить всегда сложнее...
Здравствуйте, aka50, Вы писали:
A>Здравствуйте, FDSC, Вы писали:
FDS>>Угу, особенно хорошо получается, когда не уверен в том, что документация по контроллеру правильная... точнее уверен, что в ней ошибка. FDS>>Тогда уже вообще не понятно, что тестируешь: устройство или драйвер для него. A>Ну от этого никто не застрахован, бывает и тесты ошибки содержут
Мда, это точно. Я однажды долго не мог найти ошибки в коде, а потом оказалось, что тест неправильные данные генерировал... сколько я времени убил потом, что бы они стали правильными! С тех пор не люблю UT.
Здравствуйте, aka50, Вы писали:
A>Здравствуйте, FDSC, Вы писали:
FDS>>Ммм. А если их сложно напечатать, что делать? FDS>>Например, 4-х мерная матрица 100х100х100х100 (помню, я так и не написал ту программу из-за того, что не смог нормально проанализировать её содержание) FDS>>Или, скажем, граф с представлением в виде списков следования? Это ж замучиться распечатывать! (я форму специальную делал)
A>А что мешает в этом случае делать дамп в файл? Тесты даже могут сравнивать его с заранее правильным файлом.
Мешает то, что никто не знает, какой он правильный. Для этого его сначала вручную посчитать надо — спасибо, не хочу.
Мне нужно отслеживать по шагам как изменяется объект. Если я буду выводить всё в файл, то смогу это сделать, но на каждом шаге меняется только малая часть объекта, причём я знаю какая, используя форму, я могу просмотреть только эту часть, а выводя в файл — мне нужно будет выводить всё или делать заморочки. Плюс ко всему, с малой частью я сразу же смогу сделать какую-то отладочную обработку, посмотреть какие свойства приобрёл тот или иной объект (т.е. сделать дополнительные вычисления). Если же я буду делать это для всех параметров — то же будет плохо. Т.е. мне всё равно нужен будет серьёзный обработчик файлы.
Т.е. речь идёт о том, что в файл информацию сохранять нет смысла, потому что легче сразу обрабатывать некоторую её часть по запросу тестировщика и выдавать в соотв. виде. Т.е. я просто забыл поесть и стал придираться к словам : мол, файлы не всегда спасают
IT>>>3. Copy/Paste vs. повторное использование. Copy/Paste — это разносчик багов, что есть плохо. Но шизиловка, когда каждые две строчки кода оформляются в виде отдельного метода ни чем не лучше. Поэтому настоящие индейцы копипейстят, но только один раз. Если некоторый фрагмент кода повторяется уже в третий раз, то это хорошая причина для оформления его в виде отдельного метода.
FDS>>Хм. А вот что делать, если мне нужно реализовать процедуру умножения транспонированных матриц (очень просто, неправда ли?). FDS>>Варианты реализации:
A>[skip]
FDS>>Какой из этих вариантов предпочтительней? FDS>>И не будет ли после того, как написал два раза одинаковый код, мучительно вспоминать, а где же я его ещё писал-то?
A>inline спасет... я так думаю в купе с шаблонами, если это С++.
А шаблоны тут зачем?
Т.е. вы предлагаете вариант со свойствами и объявлением метода доступа к элементу как inline?
Пожалуй, сойдёт. Только придётся в метод передавать указатель на метод доступа: не слишком красиво.
Вообще, я уже что-то подобное в ФП обсуждал. Только немного с другой стороны. Насколько я помню, мне всё-таки предложили довольно хорошее и, в общем-то, очевидное решение
FDS>>О! В январе так делал. Правда на Delphi, а не на C#. Сначала я написал бо-о-о-ольшую процедуру (200-300 строк). Её предназначение было — распознавание зашумлённых шестнадцатиричных чисел, написанных от руки на неровной поверхности. Дальше у меня появились предложения по улучшению... я стал писать вложенные процедуры, по 50-70 строк. Дальше это всё отказалось стабильно и хорошо работать . В итоге вся эта первоначальная процедура с вложенными функциями занимала почти 1000 строк. A>Рефакторить всегда сложнее...
А я про то же. Зачем писать крупные модули, если можно было сразу сделать мелкие? Правда, я очень торопился и постоянно менял алгоритм — это единственное, что является оправданием крупным модулям.
Здравствуйте, bkat, Вы писали:
B>Здравствуйте, FDSC, Вы писали:
IT>>>4. Настоящие индейцы не боятся длинных линейных методов, они не любят запутанных ветвистых уродцев, в которых легко прятаться багам.
FDS>>Вот с этого начнём, точнее закончим. Не согласен
FDS>>Где прятаться багам, если методы до отупения просты? Представьте себе, у вас все методы из 3-5 строк. Ну 10 максимум, обычно.
B>У тебя никогда не было такого, что каждый отдельный метод на 3-5 строк делает B>вроде все правильно, а вот в совокупности получается фигня?
Было. Это значит, что ты не понимаешь алгоритм расчётов или в одном из методов всё-таки допустил ошибку по невнимательности. Но точно такие же ошибки у меня получились бы и при использовании больших методов. Я просто специально статистику вёл в нескольких программах: ошибок стало меньше, а программировать стало субъективно (и объективно) проще.
Плюс, это может быть, если ты неправильно называешь методы: т.е. метод делает не то, что написано в его названии, или не только то. Короче говоря, тут уже вопрос не в твоей внимательности, а в твоей логике. Лично мне гораздо проще соблюдать логичность кода, чем держать в фокусе внимания большие куски кода. Плюс, отпадает необходимость программирования с использованием псевдокода: названия методов и есть этот псевдокод, остаётся только самый минимум комментариев.
Мелкие методы, к тому же, не дают сделать многих ошибок от усталости. У меня было раньше такое, что я невыспавшись писал код, разные части которого (сейчас — разные методы) просто перекрывались, совершенно случайно . Например, часть следующего действия попадала в цикл из предыдущего действия
Что касается большого количества "странствующих" по методам параметров (не помню, как они по человечески называются), то Delphi и Nemerle позволяют писать вложенные функции и кол-во этих параметров уменьшается до 0. На C++/C# всё, конечно, хуже.
Здравствуйте, FDSC, Вы писали:
FDS>Ммм. А если их сложно напечатать, что делать? FDS>Например, 4-х мерная матрица 100х100х100х100 (помню, я так и не написал ту программу из-за того, что не смог нормально проанализировать её содержание) FDS>Или, скажем, граф с представлением в виде списков следования? Это ж замучиться распечатывать! (я форму специальную делал)
И как ты в конце концов решил эту проблему?
FDS>Хм. А вот что делать, если мне нужно реализовать процедуру умножения транспонированных матриц (очень просто, неправда ли?). FDS>Варианты реализации:
... FDS>Какой из этих вариантов предпочтительней?
9. Настоящие индейцы руководствуются прежде всего здравым смыслом.
IT>>4. Настоящие индейцы не боятся длинных линейных методов, они не любят запутанных ветвистых уродцев, в которых легко прятаться багам.
FDS>Вот с этого начнём, точнее закончим. Не согласен
FDS>Где прятаться багам, если методы до отупения просты? Представьте себе, у вас все методы из 3-5 строк. Ну 10 максимум, обычно.
Это было отражено во втором пункте.
FDS>При этом есть одно условие: название вызываемого метода говорит о том, что этот метод делает, а этот метод делает только то, что сказано в названии (это, кстати, рекомендация Макконнелла). Тогда получаем кучу вот этих самых методов, которые просто и в которых не где прятаться багам, так как, фактически, такой код представляет собой обычный псевдокод.
Просматривая реализацию ты будешь вынужден скакать по всему коду взад вперёд. Это не удобно. Если объём метода не вредит понимабельности и не грозит потенциальными багами, то искуственно разбивать его на отдельные методы не имеет смысла.
FDS>Я почему это говорю, я не так давно пробовал (на C++) писать исключительно мелкими методами, как сказал, и скорость разработки у меня повысилась приблизительно в 1,5 раза (с отладкой). При этом большое количество багов из разряда логических ошибок перекочевало в разряд "забыл вызвать метод", "при кодировании использовал неправильный алгоритм расчётов". Кстати, в последнем случае, в мелких методах гораздо лучше видно несоответствие кода используемому алгоритму и несоответствие алгоритма решаемой задачи.
Исключительно мелкие методы — это тоже крайность, но другая. Ты решил одну проблем, но получил другую. В частности, как я уже сказал, понимать такой код сложнее.
FDS>Как вы это объясните?
Что именно? То что со временем твой метод превратился сначала в несколько методов, а потом в класс? Так вроде я именно об этом и говорю. Или нет?
FDS>Кстати, таких примеров из моей личной практики программирования до фига. Я имею ввиду, преимущества мельких методов над крупными, но с комментариями над каждой частью.
Честно говоря, я так и не понял, ты споришь или соглашаешься.
FDS> Например, год назад я писал рассчёт обратной задачи кинематики плоского манипулятора с замкнутой кинематикой (на C#) и заметил ошибку в реализации метода Ньютона только когда разбил его на много мелких методов. До этого же эта ошибка существовала чуть ли не с самого начала, я о ней знал, но думал совершенно на другие части программы.
И какой вывод из этого следует?
FDS>Может быть я вас неправильно понял?
Возможно ты просто путаешь декомпозицию метода с повторным более углублённым его переосмыслением.
FDS>Вот этот уродец (он и сейчас такой ):
А что получилось в результате?
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
IT>>4. Настоящие индейцы не боятся длинных линейных методов, они не любят запутанных ветвистых уродцев, в которых легко прятаться багам.
FDS>Вот с этого начнём, точнее закончим. Не согласен
FDS>Где прятаться багам, если методы до отупения просты? Представьте себе, у вас все методы из 3-5 строк. Ну 10 максимум, обычно. FDS>При этом есть одно условие: название вызываемого метода говорит о том, что этот метод делает, а этот метод делает только то, что сказано в названии (это, кстати, рекомендация Макконнелла). Тогда получаем кучу вот этих самых методов, которые просто и в которых не где прятаться багам, так как, фактически, такой код представляет собой обычный псевдокод.
Всё это замечательно, только на практике когда таких методов несколько десятков, то уже начинаешь банально во всём этом сокровище путаться. Ведь каждый метод может иметь побочные эффекты. И часто бывает думаешь, вот здась кокой из методов: A() ил B() и C() последовательно, — вызывать. С одной стороны, A() имеет больший уровень абстракции A(), чем B() и C(), но с другой стороны, не факт, что у A() нет побочных эффектов, которые приведут к чему-то нехорошему в данном месте. Тогда мы идём в A() и смотрим, что там имеется, и охреневаем. Потому что A() кроме вызова B() и C() содержит так же вызов D() и правит поле q. И понеслись скачки по графу программы... И это всего лишь для написания одного метода!
Конечно, мне сейчас скажут там что-то про уровни абстракции, про разделение, декомпозицию, про классы. Но на практике опять же всё сложнее. Зачастую некоторые реальные вещи бывают жутко связанными. Можно их "разделить" искусственно, но тогда увеличится количество межклассовых вызовов, и тогда мы перейдём от запутанного графа методов к запутанному графу классов...
ЛИчно мне такая вот нехорошесть встретилась при написании грида. Конечно, кое-в-чём виноваты MS (руки им за такой кривой бандинг поотрывать мало!), но основные трудности были именно из-за специфики самого контрола. Ну не выражается красиво его логика в терминах ООП! Вообще, написание сложных контролов натолкнуло меня на мысль, что язык вроде C# не соджержит сам по себе хороших абстракций для таких вещей. Что же предложить в замен я не знаю.
... << RSDN@Home 1.2.0 alpha rev. 672>>
Re[11]: Ошибок не делает тот, кто ничего не делает
Здравствуйте, FDSC, Вы писали:
FDS>Здравствуйте, aka50, Вы писали:
A>>Здравствуйте, FDSC, Вы писали:
FDS>>>Например, 4-х мерная матрица 100х100х100х100 (помню, я так и не написал ту программу из-за того, что не смог нормально проанализировать её содержание) A>>А что мешает в этом случае делать дамп в файл? Тесты даже могут сравнивать его с заранее правильным файлом. FDS>Мешает то, что никто не знает, какой он правильный. Для этого его сначала вручную посчитать надо — спасибо, не хочу.
[skip] FDS>Т.е. речь идёт о том, что в файл информацию сохранять нет смысла, потому что легче сразу обрабатывать некоторую её часть по запросу тестировщика и выдавать в соотв. виде. Т.е. я просто забыл поесть и стал придираться к словам : мол, файлы не всегда спасают
Правильный — это для тестов, а файл — для удобства просмотра (ведь можно хоть после каждой итерации новый файл писать и потом
diff-ом его, если больше одной ячейки за итерацию меняется... Или наоброт один файл сделать и например FAR будет видеть что файл
изменился и перечитывать, а у тебя будет выглядеть как в реалтайме меняющаяся картина матрицы...
FDS>>>Какой из этих вариантов предпочтительней? FDS>>>И не будет ли после того, как написал два раза одинаковый код, мучительно вспоминать, а где же я его ещё писал-то?
A>>inline спасет... я так думаю в купе с шаблонами, если это С++. FDS>А шаблоны тут зачем? FDS>3. Записать метод с передачей в него флагов транспонированности, но тогда там тоже будет небольшой copy/paste Минус: пользователь запутывается с флагами и становится тяжело использовать метод или необходимо написать 4 доп. метода, вызывающих этот для разных вариантов перемножений
Практически все уже сказано. Только скромно добавлю, что настоящий индеец следует также и завету Кента Бека по парному программированию: зачастую свежий глаз напарника-индейца, который по умолчанию критично, в отличие от вас, расположен к наблюдаемому коду, быстро ловит проектную ошибку в вашем коде. Соответственно, необходимо избавиться от ревнительности: вашему коду полезен рефакторинг чужими руками. Причем, как заметил по себе, наиболее эффективно работаешь, когда твой напарник "посильнее" тебя — подтягиваешься до него.
Здравствуйте, IT, Вы писали:
FDS>>Или, скажем, граф с представлением в виде списков следования? Это ж замучиться распечатывать! (я форму специальную делал)
IT>И как ты в конце концов решил эту проблему?
Формой с методами обработки информации. Я ей указывал на этапе исполнения, что обрабатывать. Она обрабатывала часть данных и выдавала мне некоторый результат. А в файл выводить, там были бы сотни килобайт и ничего не понятно. Обрабатывать всё: слишком долго и опять же, много ненужных данных.
IT>9. Настоящие индейцы руководствуются прежде всего здравым смыслом.
IT>>>4. Настоящие индейцы не боятся длинных линейных методов, они не любят запутанных ветвистых уродцев, в которых легко прятаться багам.
FDS>>Где прятаться багам, если методы до отупения просты? Представьте себе, у вас все методы из 3-5 строк. Ну 10 максимум, обычно.
IT>Это было отражено во втором пункте.
Вот я и не понял. Как же он хорошо отформатирован, если он большой? Это мешает пониманию метода. В частности, я должен держать в голове большее количество переменных и функций, потому что все их вызовы вынесены на поверхность и я изучаю не только алгоритм самого метода, но и алгоритмы его частей.
IT>Просматривая реализацию ты будешь вынужден скакать по всему коду взад вперёд. Это не удобно. Если объём метода не вредит понимабельности и не грозит потенциальными багами, то искуственно разбивать его на отдельные методы не имеет смысла.
Если группировать функции, то скачки можно минимизировать.
Дело в том, что у меня объём кода всегда грозит багами Поэтому и стал об этом писать. Т.е. у вас это не так?
FDS>>Я почему это говорю, я не так давно пробовал (на C++) писать исключительно мелкими методами, как сказал, и скорость разработки у меня повысилась приблизительно в 1,5 раза (с отладкой). При этом большое количество багов из разряда логических ошибок перекочевало в разряд "забыл вызвать метод", "при кодировании использовал неправильный алгоритм расчётов". Кстати, в последнем случае, в мелких методах гораздо лучше видно несоответствие кода используемому алгоритму и несоответствие алгоритма решаемой задачи.
IT>Исключительно мелкие методы — это тоже крайность, но другая. Ты решил одну проблем, но получил другую. В частности, как я уже сказал, понимать такой код сложнее.
Вот с моей точки зрения, мешают пониманию только очень редкие скачки между функциями. Тут ведь ещё в чём вопрос.
Каждый метод можно понять без изучения вызываемых им методов, просто по названиям (не всегда, правда). Поэтому методы изучаются последовательно и скачков получается очень мало. Тем более, сами методы группируются так, чтобы они были рядом.
Поэтому, лично мне кажется, понимать такой код проще, правда если заранее знаешь, что он написан именно в том стиле, о каком я говорю: т.е. название метода полностью описывает совершаемые им действия.
FDS>> Например, год назад я писал рассчёт обратной задачи кинематики плоского манипулятора с замкнутой кинематикой (на C#) и заметил ошибку в реализации метода Ньютона только когда разбил его на много мелких методов. До этого же эта ошибка существовала чуть ли не с самого начала, я о ней знал, но думал совершенно на другие части программы.
IT>И какой вывод из этого следует?
Вывод из этого следует, что не стоило писать по вашей рекомендации. Как раз делал как вы говорили и получил незаметную ошибку.
FDS>>Может быть я вас неправильно понял?
IT>Возможно ты просто путаешь декомпозицию метода с повторным более углублённым его переосмыслением.
Нет, не путаю Если декомпозицию делать сразу, то и смысл быстрее выявляется и он лежит на поверхности. В этом весь и прикол. Что не нужно повторного более глубокого осмысления. Оно и так насатаёт
FDS>>Вот этот уродец (он и сейчас такой ):
IT>А что получилось в результате?
Сам код я менять не стал. Всё хорошо и работает, а больше он вряд ли понадобится. Т.е. он такой и есть, я в скобках написал. Но именно на эту функцию я убил больше всего (и большую часть) времени на отладке.
Здравствуйте, IT, Вы писали:
IT>1. Настоящий индеец прежде всего заходит в меню Debug и в диалоге Exceptions включает галку Thrown на CLR Exceptions для managed языков. Это позволяет сэкономить не просто хучу, а туеву хучу времени при поиске ошибок. Отсюда следствие — настоящие индейцы не используют логику на исключениях, иначе весь кайф пропадает.
Не знаю что там за ошибки такие ловятся, но по крайней мере зверя ContextSwitchDeadLock я буду включать только через свой труп — он мне столько нервов попортил... Находишь с трудом ошибку, доходишь до неё через кучу махинаций с брейкпоинтами, а потом раз и выбрасывается исключение, что мол где-то там в CLR 60 сек. прошло, перезагружай прогу... так и поседеть недолго .
IT>4. Разбиение таких задач на несколько методов, которые используются только один раз не имеет никакого смысла
Согласен, только иногда существуют такие блоки в этих линейных методах, которым очень легко придумать название отдельного метода и оно очень четко функциональность этого блока описывает... В общем в таком случае получается, что дробление метода только увеличивает понятность, т.к. с хранением этих мелких методов почему-то проблем не возникает — их легко находить, а на практике и вообще не надо их находить, т.к. достаточно посмотреть на название метода, чтобы понять, что там делается. И совершенно не важно, кто этот метод вызывает, а кто вызывается им.
IT>6. Автоматическое тестирование. Настоящие индейцы обязательно пишут тесты для тестирования библиотечного кода, который используется другими частями приложения. Прикладной код тоже иногда может автоматически тестироваться, но это может оказаться банально дорого, т.к. тестирование библиотечного кода и например UI — это принципиально разные вещи. Юнит тест для библиотеки не стоит практически ничего, подготовка и периодическое изменение теста для UI может занять больше времени, чем работа над самим UI.
Золотые слова! Только что-то сомневаюсь, что покрытие библиотечного кода юнит тестами будет отлавливать много багов. У меня лично в таком коде очень редко ошибки появляются, а те что появляются — и без ЮТ о себе заявляют очень быстро, т.к. общие методы используются часто. Но, конечно, и создавать тесты для такого кода очень просто.
Здравствуйте, aka50, Вы писали:
FDS>>Т.е. речь идёт о том, что в файл информацию сохранять нет смысла, потому что легче сразу обрабатывать некоторую её часть по запросу тестировщика и выдавать в соотв. виде. Т.е. я просто забыл поесть и стал придираться к словам : мол, файлы не всегда спасают A>Правильный — это для тестов, а файл — для удобства просмотра (ведь можно хоть после каждой итерации новый файл писать и потом A>diff-ом его, если больше одной ячейки за итерацию меняется... Или наоброт один файл сделать и например FAR будет видеть что файл A>изменился и перечитывать, а у тебя будет выглядеть как в реалтайме меняющаяся картина матрицы...
Это не матрица, но всё равно.
Просто его потом ведь ещё замучеешься анализировать. Т.е. сделал diff, нашёл, какие ячейки изменились. Но тут же стал интересоваться некоторой другой информацией. А найти её можно только линейный поиском по файлу А в рантайме ячейки хранят ссылки, которые программе легко считать и вывести информацию.
FDS>>3. Записать метод с передачей в него флагов транспонированности, но тогда там тоже будет небольшой copy/paste Минус: пользователь запутывается с флагами и становится тяжело использовать метод или необходимо написать 4 доп. метода, вызывающих этот для разных вариантов перемножений
A>Но вместо функции с параметрами делаем шаблон с параметрами и для разных параметров шаблон раскроется в соотвествующую A>функцию http://www.boost.org/libs/mpl/doc/tutorial/dimensional-analysis.html
Ужос! Хотя это не совсем то, что я думал, если я правильно понял.
Здравствуйте, konsoletyper, Вы писали:
K>Всё это замечательно, только на практике когда таких методов несколько десятков, то уже начинаешь банально во всём этом сокровище путаться. Ведь каждый метод может иметь побочные эффекты. И часто бывает думаешь, вот здась кокой из методов: A() ил B() и C() последовательно, — вызывать. С одной стороны, A() имеет больший уровень абстракции A(), чем B() и C(), но с другой стороны, не факт, что у A() нет побочных эффектов, которые приведут к чему-то нехорошему в данном месте. Тогда мы идём в A() и смотрим, что там имеется, и охреневаем. Потому что A() кроме вызова B() и C() содержит так же вызов D() и правит поле q. И понеслись скачки по графу программы... И это всего лишь для написания одного метода!
Замечательно. Я так НИКОГДА не пишу. В этом весь и смысл. Что у A нет побочных эффектов, у неё есть только тот эффект, который стоит в названии метода, он может оказаться побочным в смысле функционального подхода, но не в смысле логики метода. Иногда бывает, что всё время они лезут (побочные эффекты), но если подумать, лично пока мне всегда удавалось избавиться от них. Иногда это требует написания дополнительно класса.
При этом, если A, B и C вызываются в одном методе, то у них никак не может быть разный уровень абстракции, потому что при кодировании метода ты записываешь в коде его алгоритм, а алгоритм всегда имеет только один уровень абстракции для всех пунктов.
K>Конечно, мне сейчас скажут там что-то про уровни абстракции, про разделение, декомпозицию, про классы. Но на практике опять же всё сложнее. Зачастую некоторые реальные вещи бывают жутко связанными. Можно их "разделить" искусственно, но тогда увеличится количество межклассовых вызовов, и тогда мы перейдём от запутанного графа методов к запутанному графу классов...
Самое интересное, что именно это у меня получается на C#. Причём только на C#.
Мне хотелось бы услышать пример того, какие вещи являются жутко связанными. Честно говоря я себе это очень слабо представляю. Скорее всего это связано с уже "неправильно" разработанными сторонними библиотеками. Я не верю, что если программист может составить алгоритм работы программы, то задача уменьшения связности методов/классов не может быть разрешена. Иначе бы он просто не мог держать в голове весь этот алгоритм.
Т.е. речь идёт именно о правильной декомпозиции.
K>ЛИчно мне такая вот нехорошесть встретилась при написании грида. Конечно, кое-в-чём виноваты MS (руки им за такой кривой бандинг поотрывать мало!), но основные трудности были именно из-за специфики самого контрола. Ну не выражается красиво его логика в терминах ООП! Вообще, написание сложных контролов натолкнуло меня на мысль, что язык вроде C# не соджержит сам по себе хороших абстракций для таких вещей. Что же предложить в замен я не знаю.
Можно подробнее. Я не очень понимаю, в чём там дело. А уж по C# я тут выше уже вопил (мне даже смайликов наставили), что не получается на нём нормально программировать
Здравствуйте, _Mihail, Вы писали:
IT>>1. Настоящий индеец прежде всего заходит в меню Debug и в диалоге Exceptions включает галку Thrown на CLR Exceptions для managed языков. Это позволяет сэкономить не просто хучу, а туеву хучу времени при поиске ошибок. Отсюда следствие — настоящие индейцы не используют логику на исключениях, иначе весь кайф пропадает.
_M>Не знаю что там за ошибки такие ловятся,
Это самый быстрый способ локализовать проблему. Быстрее просто не бывает.
_M>но по крайней мере зверя ContextSwitchDeadLock я буду включать только через свой труп — он мне столько нервов попортил...
Не включай. Я лично вообще такого исключения ни разу в жизни не видел.
IT>>4. Разбиение таких задач на несколько методов, которые используются только один раз не имеет никакого смысла
_M>Согласен, только иногда существуют такие блоки в этих линейных методах, которым очень легко придумать название отдельного метода и оно очень четко функциональность этого блока описывает... В общем в таком случае получается, что дробление метода только увеличивает понятность, т.к. с хранением этих мелких методов почему-то проблем не возникает — их легко находить, а на практике и вообще не надо их находить, т.к. достаточно посмотреть на название метода, чтобы понять, что там делается. И совершенно не важно, кто этот метод вызывает, а кто вызывается им.
Да ради бога. Я лишь имею в виду, что разделение на методы ради разделения на методы там где это не нужно, это так же плохо как и неразделение на методы, там где это нужно.
_M>Золотые слова! Только что-то сомневаюсь, что покрытие библиотечного кода юнит тестами будет отлавливать много багов. У меня лично в таком коде очень редко ошибки появляются, а те что появляются — и без ЮТ о себе заявляют очень быстро, т.к. общие методы используются часто. Но, конечно, и создавать тесты для такого кода очень просто.
Редкость и быстрота отлова — это плохие отговорки. Достаточно однажды, например, написать новый функционал, который слегка где-нибудь сломает старый, закомитить результат и уехать в отпуск на месяц. Если такой баг заблокирует работу нескольких человек, то либо они достанут тебя с берега лазурного моря и испортят весь кайф, либо порвут тебе жопу на немецкий крест когда ты вернёшься. Другими словами, дело не в простоте отлова и редкости. Дело в уровне ответственности. Чем чаще используется один и тот же код, тем больше должно быть контроля, что его изменение не приведёт к фатальным или блокирующим последствиям.
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, FDSC, Вы писали:
IT>>И как ты в конце концов решил эту проблему?
FDS>Формой с методами обработки информации.
Т.е. написал для отладки отдельный гуй?
IT>>Это было отражено во втором пункте.
FDS>Вот я и не понял. Как же он хорошо отформатирован, если он большой?
Форматирование кода и его размер никак не пересекаются. Под форматированием подразумеваются отступы, выравнивания и прочая мелочь, делающая код лучше для восприятия.
FDS>Это мешает пониманию метода. В частности, я должен держать в голове большее количество переменных и функций, потому что все их вызовы вынесены на поверхность и я изучаю не только алгоритм самого метода, но и алгоритмы его частей.
Всё же я не пойму с чем ты споришь. Если тебя начинает что-то напрягать, то бери и выноси в отдельные методы и классы. Но делать это только ради того, чтобы вынести каждые две строчки в отдельный метод не имеет никакого смысла.
Повторю свою мысль ещё раз. Нет проблем с размером методов, есть проблемы, которые пораждают запутанный код этих методов. Если код линеен и прозрачен, то смысл разделять длинный метод на много методов не имеет смысла. Так понятнее?
FDS>Если группировать функции, то скачки можно минимизировать. FDS>Дело в том, что у меня объём кода всегда грозит багами Поэтому и стал об этом писать. Т.е. у вас это не так?
Объём кода никуда не девается. Уменьшить объём кода можно только повторным его использованием и принципиально другими алгоритмами. Декомпозиция метода не уменьшает кода, декомпозиция позволяет выделить небольшие более менее самодостаточные блоки. Но, во-первых, это не обязательно делать методами, во-вторых, это может привести к передаче контекста, что выльется в длинный список параметров.
Кстати, один из способов, позволяющих решить эту проблему, это локальные функции. К сожалению, в C# этого нет, и пока, как я понял не планируется.
IT>>И какой вывод из этого следует?
FDS>Вывод из этого следует, что не стоило писать по вашей рекомендации. Как раз делал как вы говорили и получил незаметную ошибку.
Не факт, что ты получил незаметную ошибку именно по-этому.
IT>>Возможно ты просто путаешь декомпозицию метода с повторным более углублённым его переосмыслением.
FDS>Нет, не путаю Если декомпозицию делать сразу, то и смысл быстрее выявляется и он лежит на поверхности. В этом весь и прикол. Что не нужно повторного более глубокого осмысления. Оно и так насатаёт
Убейте меня застрелите. Я не понимаю как декомпозиция метода позволяет мне глубже осмыслить алгоритм над которым я работаю
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, FDSC, Вы писали:
FDS>Здравствуйте, aka50, Вы писали:
FDS>Это не матрица, но всё равно. FDS>Просто его потом ведь ещё замучеешься анализировать. Т.е. сделал diff, нашёл, какие ячейки изменились. Но тут же стал интересоваться некоторой другой информацией. А найти её можно только линейный поиском по файлу А в рантайме ячейки хранят ссылки, которые программе легко считать и вывести информацию.
Согласен, зависит от задачи... иногда проще целый GUI прикрутить, чтобы понять... я так в дипломе делал...
Здравствуйте, IT, Вы писали:
IT>Если такой баг заблокирует работу нескольких человек, то либо они достанут тебя с берега лазурного моря и испортят весь кайф, либо порвут тебе жопу на немецкий крест когда ты вернёшься.
Здравствуйте, FDSC, Вы писали:
K>>Всё это замечательно, только на практике когда таких методов несколько десятков, то уже начинаешь банально во всём этом сокровище путаться. Ведь каждый метод может иметь побочные эффекты. И часто бывает думаешь, вот здась кокой из методов: A() ил B() и C() последовательно, — вызывать. С одной стороны, A() имеет больший уровень абстракции A(), чем B() и C(), но с другой стороны, не факт, что у A() нет побочных эффектов, которые приведут к чему-то нехорошему в данном месте. Тогда мы идём в A() и смотрим, что там имеется, и охреневаем. Потому что A() кроме вызова B() и C() содержит так же вызов D() и правит поле q. И понеслись скачки по графу программы... И это всего лишь для написания одного метода!
FDS> Замечательно. Я так НИКОГДА не пишу. В этом весь и смысл. Что у A нет побочных эффектов, у неё есть только тот эффект, который стоит в названии метода, он может оказаться побочным в смысле функционального подхода, но не в смысле логики метода. Иногда бывает, что всё время они лезут (побочные эффекты), но если подумать, лично пока мне всегда удавалось избавиться от них. Иногда это требует написания дополнительно класса.
FDS>При этом, если A, B и C вызываются в одном методе, то у них никак не может быть разный уровень абстракции, потому что при кодировании метода ты записываешь в коде его алгоритм, а алгоритм всегда имеет только один уровень абстракции для всех пунктов.
В том то всё и дело, что когда у тебя много методов, у тебя много уровней абстракции. A() имеет более высокий уровень, чем B() и C(), потому ты начинаешь путаться при написании нового метода, из каких именно абстракций исходит логика его работы. Это всё при том, что нельзя всё совершенно чётко поделить на уровнии, где-то они могут частично перекрываться.
Казалось бы, для уровней абстракции можно писать отдельные классы, но тогда приходим к запутанности на уровне классов и т.д.
FDS>Мне хотелось бы услышать пример того, какие вещи являются жутко связанными. Честно говоря я себе это очень слабо представляю. Скорее всего это связано с уже "неправильно" разработанными сторонними библиотеками. Я не верю, что если программист может составить алгоритм работы программы, то задача уменьшения связности методов/классов не может быть разрешена. Иначе бы он просто не мог держать в голове весь этот алгоритм.
Ну, программирование редко сводится к осознанию алгоритмов. Уже тем более, проектирвание мало связанно с алгоритмизацией (если это только не специфическая алгоритмически нетривиальная задача). И в голове мало кто что держит. Ведь у человека временная память расчитана в среднем на 7 объектов.
K>>ЛИчно мне такая вот нехорошесть встретилась при написании грида. Конечно, кое-в-чём виноваты MS (руки им за такой кривой бандинг поотрывать мало!), но основные трудности были именно из-за специфики самого контрола. Ну не выражается красиво его логика в терминах ООП! Вообще, написание сложных контролов натолкнуло меня на мысль, что язык вроде C# не соджержит сам по себе хороших абстракций для таких вещей. Что же предложить в замен я не знаю.
FDS>Можно подробнее. Я не очень понимаю, в чём там дело. А уж по C# я тут выше уже вопил (мне даже смайликов наставили), что не получается на нём нормально программировать
Я тоже не совсем понимаю, в чём дело. Когда приступил к проектированию, всё было вроде бы хорошо. Но вот когда написал реализацию, получил запутанный код. Это при том, что и до и после грида я писал разные вещи, и никогда не приходил к такой же путаннице. Постараюсь на выходных провести исследование и ответить на этот вопрос.
Здравствуйте, FDSC, Вы писали:
FDS>Дело в том, что если бы я писал функцию сохранения в файл на C++ я бы написал всё с нуля, но вызов был бы в одну строку. FDS>А, скажем, для сериализации мне необходимо (вместо пары сотен строк) FDS>1. Создать поток, связанный с файлом, открытым в правильном режиме (одна строка) FDS>2. Создать форматтер (одна строка) FDS>3. Передать в форматтер сериализуемый объект.
Гм. Если у тебя этот код встречается часто, то можно сделать отдельный хелперный метод, и вызов тоже будет в одну строку.
FDS>Вот я и говорю: я просто нетерпелив. Мне мешает то, что это занимает 3 строки
FDS>При сравнении строк примерно то же: я должен создать итератор, что бы обращаться к строке независимо от кодировки,
Вот этот набор слов я не понял. Какой итератор? Что создать? Всё делается в одну строку:
Чего еще? FDS>затем я должен выбрать каким образом я должен сравнивать строки (т.е. настройки локализации) и т.д.
FDS>Само по себе это всё идёт плюс несколько строк в код, но экономит, за счёт того, что это сделано, очень много кода. Но то, что я должен думать о том, что у меня в using не стоит System.IO при выполнении сериализации, которая якобы реализована, лично меня очень сильно раздражает.
Ничего ты не должен. Когда ты набираешь что-то типа File.Open() тебе редактор сразу подчеркивает File и предлагает добавить using. Если я не всё забыл, то делается это Alt+F10, Enter. Всё, FDS>Именно тем, что думаешь уже высокоуровневыми терминами и когда тебе компилятор выдаёт ошибку, что он не нашёл соотв. namespace System.IO, ты как-будто об стенку грохаешься. Т.е. я бы ожидал отдельно для бинарной сериализации некоторый конструктор форматтера, который бы брал все необходимые параметры и не заставлял создавать мне поток, который я, скорее всего, после сериализации сразу же закрою.
Ну почему бы тебе не сделать этот "конструктор"? Это пара строк кода — и твои проблемы решены.
Я бы понял, если бы ты испытывал такие чувства на второй неделе знакомства с языком. Но ты же уже чуть ли не полгода с ним работаешь... Что тебе мешает учиться правильно им пользоватьcя?
FDS>Соотв., пример кода можно взять даже из rsdn.editor или любой другой программы: я специально смотрел код других программистов. Насколько я помню, там Влад (и кто там ещё?) всё делал как раз в таком же духе...
1.2.0 alpha rev. 655
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, FDSC, Вы писали:
FDS>Здравствуйте, IT, Вы писали:
IT>>Несколько практических советов от настоящих индейцев.
FDS>
IT>>7. Ошибки происходят не только из-за неправильного кода, но и из-за неправильных данных, которые впрочем могут быть порождены неправильным кодом. Настоящие индейцы используют логи и вывод отладочной печати в отладочное окно студии. Зачастую сложную структуру данных проще напечатать и иметь сразу всю картину, чем бродить по окну Watch, заглядывая по очереди во все переменные класса.
FDS>Ммм. А если их сложно напечатать, что делать? FDS>Например, 4-х мерная матрица 100х100х100х100 (помню, я так и не написал ту программу из-за того, что не смог нормально проанализировать её содержание) FDS>Или, скажем, граф с представлением в виде списков следования? Это ж замучиться распечатывать! (я форму специальную делал)
IT>>3. Copy/Paste vs. повторное использование. Copy/Paste — это разносчик багов, что есть плохо. Но шизиловка, когда каждые две строчки кода оформляются в виде отдельного метода ни чем не лучше. Поэтому настоящие индейцы копипейстят, но только один раз. Если некоторый фрагмент кода повторяется уже в третий раз, то это хорошая причина для оформления его в виде отдельного метода.
FDS>Хм. А вот что делать, если мне нужно реализовать процедуру умножения транспонированных матриц (очень просто, неправда ли?).
FDS>Какой из этих вариантов предпочтительней?
Правильный вариант: сделать простой транспонирующий wrapper. Примерно так:
public interface IMatrix<T>
{
T this[x, y] { get; set;}
int Width { get; }
int Height { get; }
}
public class TransposedMatrix<T>: IMatrix<T>
{
private IMatrix<T> _source;
protected IMatrix<T> Source { get { return _source; }}
private TransposedMatrix(IMatrix<T> source)
{
_source = source;
}
public IMatrix<T> Transpose(IMatrix<T> source)
{
// avoid double transposing:
TransposedMatrix<T> tr = source as TransposedMatrix<T>;
if (tr!=null)
return tr.Source;
return new TransposedMatrix<T>(source);
}
#region IMatrix<T> implementation
public T this[x, y]
{
get { return _source[y, x]; }
set { _source[y, x] = value; }
}
int Width { get { return _source.Height; } }
int Height { get { return _source.Width; } }
#endregion
}
Предполагаю, что реализация обычной матрицы тривиальна. Теперь, всякий раз когда тебе надо умножить A на Bt, ты так и пишешь A * TransposedMatrix.Transpose(B).
Производительность, скорее всего, будет не слишком хорошей (на текущей версии CLR). Поэтому подобные фрагменты имеет смысл оформить на С++; его компилятор выполнит агрессивный инлайнинг и вся абстрактность уйдет — результирующий код будет не медленнее, чем то, что ты напишешь каждый раз руками.
1.2.0 alpha rev. 655
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, FDSC, Вы писали:
FDS>Сейчас 2005. Что такое смартеги даже не знаю Ээээ, может кто-нибудь подскажет?
Ты когда набираешь тип, который студии известен, но для него нет using, то в конце имени типа внизу появляется маленький красненький прямоугольник. Если щелкнуть по нему мышой или нажать горячую клавишу (Alt-Shift-F10 по дефолту, кажется), то нужный using будет добавлен автоматом.
Ну или поставь решарпер. Там ты подсказку не пропустишь
Здравствуйте, bkat, Вы писали:
B>Например так. B>Порядок (тесты->код или наоборот) для меня особой роли не играет.
Тогда это не юнит-тесты
B>Конкретным он был бы, если бы вместе работали над одним проектом.
По твоему получается, что вобще ничего обсудать нельзя, так что ли?
B>А без конкретики, нужны ли юнит-тесты или не нужны — спор не очень конструктивен.
Но это не мешает делать заявлений вроде "ну вобще почти всегда юнит-тесты нужны".
Здравствуйте, AndrewVK, Вы писали:
AVK>У IT немного иное понимание unit-тестирования. В свое время мы, кажется здесь, это уже обсуждали и пришли к консенсусу. Суть в том, что не нужно добиваться полного покрытия, нужно описывать в тестах только основные сценарии применения. Против этого (я, кажется, сегодня уже писал) я ничего не имею. Но это не совсем unit-тестирование в классическом его понимании.
Это скорее интеграционные тесты. Или приёмочные. Терминов много — я в них путаюсь
Здравствуйте, Andrei N.Sobchuck, Вы писали:
ANS>Здравствуйте, AndrewVK, Вы писали:
AVK>>У IT немного иное понимание unit-тестирования. В свое время мы, кажется здесь, это уже обсуждали и пришли к консенсусу. Суть в том, что не нужно добиваться полного покрытия, нужно описывать в тестах только основные сценарии применения. Против этого (я, кажется, сегодня уже писал) я ничего не имею. Но это не совсем unit-тестирование в классическом его понимании.
ANS>Это скорее интеграционные тесты. Или приёмочные. Терминов много — я в них путаюсь
Здравствуйте, FDSC, Вы писали:
A>>А что мешает в этом случае делать дамп в файл? Тесты даже могут сравнивать его с заранее правильным файлом.
FDS>Мешает то, что никто не знает, какой он правильный. Для этого его сначала вручную посчитать надо — спасибо, не хочу.
Тесты это способ фиксации собственных знаний. Если ты чего-то не знаеш, то тесты на такой случай, естественно, не напишешь.
Правильность алгоритма тестом тоже не докажеш. Но своё "понимание" алгоритма в тесте зафиксировать можно.
Здравствуйте, Sinclair, Вы писали:
S>Здравствуйте, FDSC, Вы писали:
FDS>>Дело в том, что если бы я писал функцию сохранения в файл на C++ я бы написал всё с нуля, но вызов был бы в одну строку. FDS>>А, скажем, для сериализации мне необходимо (вместо пары сотен строк) FDS>>1. Создать поток, связанный с файлом, открытым в правильном режиме (одна строка) FDS>>2. Создать форматтер (одна строка) FDS>>3. Передать в форматтер сериализуемый объект. S>Гм. Если у тебя этот код встречается часто, то можно сделать отдельный хелперный метод, и вызов тоже будет в одну строку.
FDS>>Вот я и говорю: я просто нетерпелив. Мне мешает то, что это занимает 3 строки
FDS>>При сравнении строк примерно то же: я должен создать итератор, что бы обращаться к строке независимо от кодировки, S>Вот этот набор слов я не понял. Какой итератор? Что создать? Всё делается в одну строку: S>
Важно, что я часто натыкаюсь на такие вещи, когда нужно писать обёртку над классом и сам C# провоцирует написание классов, требующих обёрток.
Ну, лично моё субъективное, видимо, заблуждение.
FDS>>Именно тем, что думаешь уже высокоуровневыми терминами и когда тебе компилятор выдаёт ошибку, что он не нашёл соотв. namespace System.IO, ты как-будто об стенку грохаешься. Т.е. я бы ожидал отдельно для бинарной сериализации некоторый конструктор форматтера, который бы брал все необходимые параметры и не заставлял создавать мне поток, который я, скорее всего, после сериализации сразу же закрою. S>Ну почему бы тебе не сделать этот "конструктор"? Это пара строк кода — и твои проблемы решены.
Дык я его и делаю, это просто пример пришёл первый на ум. Просто постоянно такие ощущения возникают по всяким мелочам. Т.е. приходиться думать над небольшими обёртками для .NET классов. Не удобно. Хотя, можно привыкнуть, наверное.
S>Я бы понял, если бы ты испытывал такие чувства на второй неделе знакомства с языком. Но ты же уже чуть ли не полгода с ним работаешь... Что тебе мешает учиться правильно им пользоватьcя?
Я работаю с ним уже больше года. Вот и сам не пойму, что мешает.
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, FDSC, Вы писали:
FDS>>Сейчас 2005. Что такое смартеги даже не знаю Ээээ, может кто-нибудь подскажет?
AVK> AVK>Ты когда набираешь тип, который студии известен, но для него нет using, то в конце имени типа внизу появляется маленький красненький прямоугольник. Если щелкнуть по нему мышой или нажать горячую клавишу (Alt-Shift-F10 по дефолту, кажется), то нужный using будет добавлен автоматом. AVK>Ну или поставь решарпер. Там ты подсказку не пропустишь
Спасибо большое Вот так всегда — не купишь книжку по очередной версии студии и что-нибудь обязательно не заметишь
Но тут всё равно важен не столько using, сколько даже наличие мыслей о, скажем, потоках ввода/вывода.
Здравствуйте, konsoletyper, Вы писали:
K>В том то всё и дело, что когда у тебя много методов, у тебя много уровней абстракции. A() имеет более высокий уровень, чем B() и C(), потому ты начинаешь путаться при написании нового метода, из каких именно абстракций исходит логика его работы. Это всё при том, что нельзя всё совершенно чётко поделить на уровнии, где-то они могут частично перекрываться. K>Казалось бы, для уровней абстракции можно писать отдельные классы, но тогда приходим к запутанности на уровне классов и т.д.
Я вас не понимаю. Видимо, у нас слишком разные области применения программирования.
K>Ну, программирование редко сводится к осознанию алгоритмов. Уже тем более, проектирвание мало связанно с алгоритмизацией (если это только не специфическая алгоритмически нетривиальная задача). И в голове мало кто что держит. Ведь у человека временная память расчитана в среднем на 7 объектов.
Проектирование — это всегда алгоритмизация. Другой вопрос, насколько она сложная.
Фактически, когда вы проектируете вы думаете, что вам нужно сделать и проектируете для реализации вашего алгоритма классы, которые потом и соберутся в требуемые действия. Иначе вы вообще не сможете проектировать. Алгоритм использования приложения (или его части) всегда в ваших мозгах.
FDS>>Можно подробнее. Я не очень понимаю, в чём там дело. А уж по C# я тут выше уже вопил (мне даже смайликов наставили), что не получается на нём нормально программировать
K>Я тоже не совсем понимаю, в чём дело. Когда приступил к проектированию, всё было вроде бы хорошо. Но вот когда написал реализацию, получил запутанный код. Это при том, что и до и после грида я писал разные вещи, и никогда не приходил к такой же путаннице. Постараюсь на выходных провести исследование и ответить на этот вопрос.
Здравствуйте, Andrei N.Sobchuck, Вы писали:
ANS>Здравствуйте, Ракопаукодав, Вы писали:
Р>>Это советы для кодирования, а спрашивали рекомендации по проектированию
ANS>1. Семь раз отмерь — один отрежь.
А чем мерять? В смысле, какие средства при проектировании использовать?
P.S. Опять же, если в фирме начальство строго относится к пиратскому ПО, то просто так не попроектируешь...
Здравствуйте, FDSC, Вы писали:
FDS>А чем мерять? В смысле, какие средства при проектировании использовать? FDS>P.S. Опять же, если в фирме начальство строго относится к пиратскому ПО, то просто так не попроектируешь...
Ну почему-же... полно бесплатных или достаточно недорогих продуктов... вопрос только насколько навороченная среда нужна . Например можно что-то поднобное http://argouml.tigris.org/features.html найти...
Здравствуйте, IT, Вы писали:
IT>Кстати, один из способов, позволяющих решить эту проблему, это локальные функции. К сожалению, в C# этого нет, и пока, как я понял не планируется.
Ну можно это будет лямбдами сэмулировать. Хотя для 3х уровней вложенности это ужастик получится, да.
"Если Вы отличаетесь от меня, то это ничуть мне не вредит — Вы делаете меня богаче". Экзюпери
Здравствуйте, FDSC, Вы писали:
FDS>А чем мерять? В смысле, какие средства при проектировании использовать?
FDS>P.S. Опять же, если в фирме начальство строго относится к пиратскому ПО, то просто так не попроектируешь...
Проектировать — это не значит рисовать что-нибудь в какой-нибудь навороченной рисовалке схем. Можно сказать, что спроектировать — это написть ту же самую программу, но с очень низкой степенью детализации. А уж "программировать" с низкой степенью детализации можно разными способами (ИМХО — в порядке убывания полезности):
1) Используя привычный язык программирования для описания основных интерфейсов, классов, и, возможно, программного кода, который в очень крупном масштабе записывает самые важные и крупные действия, которые должны происходить в программе.
2) Написать программу в крупном масштабе можно на бумажке карандашом, либо в специализированной рисовалке произвользых схем, используя при этом какие угодно картинки.
3) UML в специализированном редакторе (RR, Together, и т.д.)
Здравствуйте, Mirrorer, Вы писали:
M>Здравствуйте, IT, Вы писали:
IT>>Кстати, один из способов, позволяющих решить эту проблему, это локальные функции. К сожалению, в C# этого нет, и пока, как я понял не планируется.
M>Ну можно это будет лямбдами сэмулировать. Хотя для 3х уровней вложенности это ужастик получится, да.
В C# такие лямбды, что уж лучше контекст передавать или прямо сразу доп. класс сделать. Не смотрятся там лямбды как повсеместная замена вложенных функций.
Здравствуйте, IT, Вы писали:
FDS>>Формой с методами обработки информации. IT>Т.е. написал для отладки отдельный гуй?
Угу . Я так часто делаю, когда возникают проблемы с отладкой
IT>Всё же я не пойму с чем ты споришь. Если тебя начинает что-то напрягать, то бери и выноси в отдельные методы и классы. Но делать это только ради того, чтобы вынести каждые две строчки в отдельный метод не имеет никакого смысла. IT>Повторю свою мысль ещё раз. Нет проблем с размером методов, есть проблемы, которые пораждают запутанный код этих методов. Если код линеен и прозрачен, то смысл разделять длинный метод на много методов не имеет смысла. Так понятнее?
Вот с этим и спорю , я это так и понял в первый раз. Видимо вы гораздо более аккуратно программируете, раз у вас всё нормально.
Я выделяю метод, если вижу, что некоторая часть кода выполняет то, что можно назвать отдельной функцией.
Т.е. есть я не согласен с этим утверждением, которое вы считаете мелочью, а для меня (и, судя по студентам, которые сейчас на первом курсе изучают программирование, для них тоже) это не мелочь.
IT>Не факт, что ты получил незаметную ошибку именно по-этому.
Хм. Факт . Метод был вполне нормально форматирован, да и писал я эту реализацию уже не в первый раз. При разбивке сразу стала видна нелогичность.
Вот представьте себе, что у вас длинный метод и есть две переменных со схожим назначением и содержимым. (это условный пример)
В один прекрасный момент вы эти две переменных путаете (возможно, они, конечно, названы не слишком удачно), а метод такой большой, что при просмотре кода с начала вы забываете предназначение и разницу между двумя переменными. А если все блоки кода скрыты в методах, то эти две переменные уже лежат на поверхности и их смысл очевиден на протяжении всего метода.
IT>Убейте меня застрелите. Я не понимаю как декомпозиция метода позволяет мне глубже осмыслить алгоритм над которым я работаю
А вы всегда выписываете алгоритм действий на бумажке? Или хотя бы на псевдокоде? И всегда после этого проверяете соответствие кода тому, что написано на бумажке?
Если хотя бы писать сначало всё на псевдокоде — то, убейте меня лучше сразу — не легче ли написать вместо псевдокода сразу названия методов? По крайней мере тогда большинство методов будут списаны с бумажки и мелкой ошибке будет закрасться труднее.
А смысл "семь раз отмерь — один отрежь" — перед тем как писать, нужно подумать. Я говорю не об конструировании иерархии (ибо сам не умею зараннее классы продумывать), а о понимании самой задачи. Непонимание задачи делает бессмысленым любые архитектурные изыски. Если есть сомнения, то сделай прототип(ы). Решение обычно оформляется, когда ты наберёшь некоторую критическую массу знаний, как по самой задаче, так и по технологиям, которые можно применить.
. Когда говорят, если бы было время я бы сделал красиво, а так как его не было, сделал уродливо — не верь. Красивое решение делается за минимальное время и минимальным количеством усилий. Именно по этому оно и красиво. На микроуровне можно пользоваться формальными правилами типа LSP или закона Деметра (есть, кстати, подобные правила для ФЯ?), но макроуровне тебе поможет только твоё чувство прекрасного.
3. Лучшее враг хорошего. Если тебе кажется, что ты придумал ерунду, но незнаешь как сделать лучше, не бойся воплощать решение в жизнь, так как кривое решение лучше чем никакого. Со временем у тебя появятся нужные знания (если ты к ним стремишся, естественно), и ты недопустишь подобных просчетов.
4. Практика — критерий истины. Если думаеш, что ты сделал "красоту", а оно не работает, то меняй своё чувство прекрасного. То биш нужна обратная связь между твоим решением и твоей же думалкой.
5. Одна голова хорошо, а две лучше. Даже если ты архитектор. не стейсняйся спрашивать у простых программистов их мнение. Общайся, сверяй чувство прекрасного у других со своим.
Получается, что все, что нужно это твоя голова. Если её нет, то никакая тулза не поможет Для усовершенствования работы головы есть разные методики, типа ТРИЗа. Другое дело, я не знаю, насколько они помогают Ну, и практика-обратная связь, практика-обратная связь, практика-обратная связь.
Я еще пользуюсь универсальными философскими правилами:
1. Разделяй и властвуй. Следствие из особенности человеческого мозга "7+-2". Поскольку всю архитект`уру в целом тяжело удержать в голове, то задачу бьют на части. Тут нужно читать о функциональной/объектной декомпозиции. Кстати, вывод: чем меньше ответсвенности на классе (или другой единице декомпозиции), и с чем меньшим числом других классов идёт взаимодействие, тем его проще и придумать и реализовать.
2. Стой на плечах у гигантов. Применяй, то что придумали до тебя другие. Есть там правила, типа "Первое правило создания распределённых приложений — не распределяй". Ну, и прочая мудрость, которая поможет тебе не ходить по всеизвестным граблям. Мне, например, нравится ОО: тотальный messaging, рекурсивный дизайн. Постепенно всё идёт, как минимум к тотальному messaging, кроме того есть правила, которые удовлетворяют моему чувству прекрасного. Так что я вижу смысл пользоваться этим прямо сейчас, но я не навязываю
Здравствуйте, FDSC, Вы писали:
FDS>P.S. Опять же, если в фирме начальство строго относится к пиратскому ПО, то просто так не попроектируешь...
Что нормально проектировать можно только в тулах за многие килобаксы?
Нормально проектировать можно и на листочке бумаги.
Если нету светлых мыслей, то никакой тул не поможет.
Уже сколько раз наблюдал, как народ свято верил,
что с появлением крутого тула (вполне хорошего, кстати)
все проблемы со спецификациями, дизайном и пр... уйдут автоматически.
А вот не уходят...
Фигня, поданная на вход самому замечательному тулу, не перестает быть фигней...
Здравствуйте, prVovik, Вы писали:
V>Здравствуйте, FDSC, Вы писали:
FDS>>А чем мерять? В смысле, какие средства при проектировании использовать?
FDS>>P.S. Опять же, если в фирме начальство строго относится к пиратскому ПО, то просто так не попроектируешь...
V>Проектировать — это не значит рисовать что-нибудь в какой-нибудь навороченной рисовалке схем. Можно сказать, что спроектировать — это написть ту же самую программу, но с очень низкой степенью детализации. А уж "программировать" с низкой степенью детализации можно разными способами (ИМХО — в порядке убывания полезности): V>1) Используя привычный язык программирования для описания основных интерфейсов, классов, и, возможно, программного кода, который в очень крупном масштабе записывает самые важные и крупные действия, которые должны происходить в программе.
Угу, только вот почему-то потом эти действия всегда оказываются не такими Да и если записывать такие действия в коде, не видно, кто к кому обращается, очень велик риск потерять информационные связи.
Здравствуйте, Andrei N.Sobchuck, Вы писали:
ANS>А смысл "семь раз отмерь — один отрежь" — перед тем как писать, нужно подумать. Я говорю не об конструировании иерархии (ибо сам не умею зараннее классы продумывать), а о понимании самой задачи. Непонимание задачи делает бессмысленым любые архитектурные изыски. Если есть сомнения, то сделай прототип(ы). Решение обычно оформляется, когда ты наберёшь некоторую критическую массу знаний, как по самой задаче, так и по технологиям, которые можно применить.
Не поверишь. Буквально в конце августа я писал одну прогу на C#, смысл которой был в предоставлении интерфейса подключаемым библиотекам и управлении данными этих библиотек (т.е. сохранение доп. информации этих библиотек в том же файле, что и осн. информация).
Ну вроде всё описал. На бумаге (*.txt) столько исписал всего, что даже смотреть страшно. Всё равно, когда я начал кодировать загрузку межбиблиотечных данных (самое последнее, что надо было сделать), оказалось, что я: 1. Не сохранил нужную информацию о обработчике информации 2. Вообще вся необходимая информация находится в другом модуле, является приватной и ещё не загружена 3. При десериализации порядок вызова методов обработчиков информации неправильный и приводит к ошибкам. И вот тут мне было очень плохо
А ведь расписал всё так, казалось, что неожиданностей быть не должно. А вот нет, упустил.
ANS>3. Лучшее враг хорошего. Если тебе кажется, что ты придумал ерунду, но незнаешь как сделать лучше, не бойся воплощать решение в жизнь, так как кривое решение лучше чем никакого. Со временем у тебя появятся нужные знания (если ты к ним стремишся, естественно), и ты недопустишь подобных просчетов.
Только после таких просчётов руки опускаются Особенно, когда пять штук подряд. Тут уже можно и вообще ничего не сделать.
ANS>Получается, что все, что нужно это твоя голова. Если её нет, то никакая тулза не поможет Для усовершенствования работы головы есть разные методики, типа ТРИЗа. Другое дело, я не знаю, насколько они помогают Ну, и практика-обратная связь, практика-обратная связь, практика-обратная связь.
К сожалению, всё это мало помогает. Вопрос ведь не в том, что бы знать эти правила, а в том, чтобы получалось их соблюдать при проектировании. Потому что страешься их соблюдать, а потом оказывается, что не соблюл
Здравствуйте, bkat, Вы писали:
B>Что нормально проектировать можно только в тулах за многие килобаксы? B>Нормально проектировать можно и на листочке бумаги. B>Если нету светлых мыслей, то никакой тул не поможет.
Здравствуйте, FDSC, Вы писали:
FDS>Здравствуйте, bkat, Вы писали:
B>>Что нормально проектировать можно только в тулах за многие килобаксы? B>>Нормально проектировать можно и на листочке бумаги. B>>Если нету светлых мыслей, то никакой тул не поможет.
FDS>Может расскажите, как "нормально проектировать"?
Хм...
Думаешь это влезет в 10 строк сообщения на форуме?
Ты точно так же можешь попросить рассказать о том, как писать хорошие программы.
Советы их книжек ты и сам наверняка знаешь...
А все остальное — это опыт, который ты сам должен получить.
Не думаю, что можно научтися успешно проектировать прочитав какую-нибудь "нужную" книгу либо другой материал по соответствующей тематике.
Литература всего лишь описывает возможные рецепты и модели проектирования, которые были сформулированы и использованы в возможно успешных проектах.
Основной целью подобной литературы являются:
1. Показать как можно это сделать.
2. Указать существующие модели и паттерны.
3. Заставить мозг думать.
Уверен, однако, что как бы внимательно вы не изучали подобную литературу, некоторые моменты вы обязательно упустите или не обратите внимания.
Научиться "нормально проектировать" возможно только путем собственных проб и ошибок, чем больше бы будете пробовать, тем опытнее вы будете.
Здравствуйте, FDSC, Вы писали:
FDS> 1. Не сохранил нужную информацию о обработчике информации
К архитектуре это не имеет отношения.
FDS> 2. Вообще вся необходимая информация находится в другом модуле, является приватной и ещё не загружена
Вот-вот. Этого ты не знал. А мог бы написать тест, который выводил список доступной тебе информации в модуле. Но, опять же, какое влияние на архитектуру.
FDS>3. При десериализации порядок вызова методов обработчиков информации неправильный и приводит к ошибкам. И вот тут мне было очень плохо
Пиши тесты
FDS> К сожалению, всё это мало помогает. Вопрос ведь не в том, что бы знать эти правила, а в том, чтобы получалось их соблюдать при проектировании. Потому что страешься их соблюдать, а потом оказывается, что не соблюл
Работай над собой
Кстати, есть методики позволяющие "менять" себя. Типа всяких "ауто-", НЛП. Так вот, у них у всех есть одна общая черта, перед тем как работать над собой нужно обдумать, что ты хочеш получить (обычно записать на бумаге на бумаге), поработать над собой, записать что получилось. Новый цикл. Только это не проблемы архитектуры.
Здравствуйте, Mirrorer, Вы писали:
IT>>Кстати, один из способов, позволяющих решить эту проблему, это локальные функции. К сожалению, в C# этого нет, и пока, как я понял не планируется.
M>Ну можно это будет лямбдами сэмулировать. Хотя для 3х уровней вложенности это ужастик получится, да.
На каждую такую функцию уморишься делегаты ообъявлять. Короче, лажа это. Без крайней необходимости никто этим пользоваться не будет.
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, Andrei N.Sobchuck, Вы писали:
ANS>Здравствуйте, FDSC, Вы писали:
FDS>> 1. Не сохранил нужную информацию о обработчике информации
ANS>К архитектуре это не имеет отношения.
Как раз имеет. Именно архитектура определяет формат данных в сохраняемом файле и от этого формата (и от того, что сохраняется, а что — нет) очень многое зависит.
FDS>> 2. Вообще вся необходимая информация находится в другом модуле, является приватной и ещё не загружена
ANS>Вот-вот. Этого ты не знал. А мог бы написать тест, который выводил список доступной тебе информации в модуле. Но, опять же, какое влияние на архитектуру.
Какой ещё тест? Я сам проектировал этот модуль, но когда проектировал и писал ещё не знал, что информация этого модуля мне понадобится. Хотя специально смотрел, какая информация понадобится, про эту забыл.
FDS>>3. При десериализации порядок вызова методов обработчиков информации неправильный и приводит к ошибкам. И вот тут мне было очень плохо
ANS>Пиши тесты
Опять же, что же это за тест, который тестирует архитектуру? Я просто взял и так спроектировал программу, что порядок загрузки информации был неправильный. Причём этот порядок определялся именно архитектурой, а не порядком вызова некоторых функций в методе. Для его исправления пришлось довольно много всего писать.
Здравствуйте, FDSC, Вы писали:
FDS>Как раз имеет. Именно архитектура определяет формат данных в сохраняемом файле и от этого формата (и от того, что сохраняется, а что — нет) очень многое зависит.
Моё понимание архитектуры отличается от твоего
А от такой "забывчивости", как ты описал никакой мегатул не спасёт.
Здравствуйте, FDSC, Вы писали:
IT>>Убейте меня застрелите. Я не понимаю как декомпозиция метода позволяет мне глубже осмыслить алгоритм над которым я работаю
FDS> FDS>А вы всегда выписываете алгоритм действий на бумажке? Или хотя бы на псевдокоде? И всегда после этого проверяете соответствие кода тому, что написано на бумажке?
На бумажке уже даже не помню когда писал. Писать начинаю в коде сразу, даже ещё не понимая что получится в результате. Единственное, что я стараюсь делать с самого начала — это писать так, чтобы код работал сразу. Т.е. пусть он ещё делает не всё, но уже делает то, что написано, чтобы это можно было запустить и посмотреть. Дальше по шагам начинаем достраивать алгоритм и с каждым таким кирпичиком проверяем результат. При таком подходе опысываемые тобой ошибки исключены.
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, Ракопаукодав, Вы писали:
Р>Это советы для кодирования, а спрашивали рекомендации по проектированию
Советую ещё раз внимательно перечитать вопрос.
ЗЫ. Я специально старался опускать вопросы проектирования и архитектуры, т.к. умных общих фраз тут и без меня навыдают. Только, к сожалению, по моим наблюдениям между умением говорить такие банальности и умением их применять лежит огромная пропасть, мостиком через которую как раз и являются практические приёмы и навыки.
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
Re[12]: Ошибок не делает тот, кто ничего не делает
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, Andrei N.Sobchuck, Вы писали:
AVK>>>А кто сказал, что он уникален?
ANS>>Хорошо. Тогда скажи в каком именно другом месте проблема.
AVK>Место называется GUI
А я как всегда думал что в генах ... Оказывается все простог , виноват противный ГУИ . Зато мне легче стало на душе И я не стану с прохладой относиться к тому что говорят люди запятнавшие себя разработкой Януса !
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Ищу работу, 3D, SLAM, computer graphics/vision.
Re[13]: Ошибок не делает тот, кто ничего не делает
Здравствуйте, red_dragon, Вы писали:
_>Здравствуйте, prVovik, Вы писали:
V>>Здравствуйте, red_dragon, Вы писали:
_>>>Проведение рефакторинга системы без юнит-тестов — это bugs*bugs при котором bugs -> infinity
V>>Несмотря на то, что я за юнит тесты, но данное высказывание более чем спорно
_>Делали рефакторинг без юнит-тестов?
_>Я делал В итоге что-бы все заработало как надо, потратил массу времени, ну и чистота кода оставляет желать лучшего
Да слышал я этого шалапина ! Мне рабинович напел , такой акцент ..
Здравствуйте, IT, Вы писали:
IT>Здравствуйте, FDSC, Вы писали:
IT>>>Убейте меня застрелите. Я не понимаю как декомпозиция метода позволяет мне глубже осмыслить алгоритм над которым я работаю
FDS>> FDS>>А вы всегда выписываете алгоритм действий на бумажке? Или хотя бы на псевдокоде? И всегда после этого проверяете соответствие кода тому, что написано на бумажке?
IT>На бумажке уже даже не помню когда писал. Писать начинаю в коде сразу, даже ещё не понимая что получится в результате. Единственное, что я стараюсь делать с самого начала — это писать так, чтобы код работал сразу. Т.е. пусть он ещё делает не всё, но уже делает то, что написано, чтобы это можно было запустить и посмотреть. Дальше по шагам начинаем достраивать алгоритм и с каждым таким кирпичиком проверяем результат.
Вот до этого места я читал с жутким одобрением и думал, что делаю точно так же
IT> При таком подходе опысываемые тобой ошибки исключены.
Здравствуйте, rameel, Вы писали:
R>Здравствуйте, FDSC, Вы писали:
FDS>>Спасибо большое Вот так всегда — не купишь книжку по очередной версии студии и что-нибудь обязательно не заметишь
R>Смарт теги реализованы не только в студии, но и в ворде и т.д., это так к сведению
Здравствуйте, FDSC, Вы писали:
FDS>Хм. А вот что делать, если мне нужно реализовать процедуру умножения транспонированных матриц (очень просто, неправда ли?). FDS>Варианты реализации:
FDS>1. Для того, что бы перемножить некоторым образом транспонированные матрицы я их транспонирую отдельной функцией и затем передаю в обычную процедуру умножения матриц FDS>Минус: понижение производительности. Ведь я мог бы перемножить матрицы без физического транспонирования
Матрицу физически транспонировать и не надо.
Сделай у матрицы флаг транспонированности, а внутри свойства доступа к элементам матрицы меняй индексы местами, если флаг транспонированности установлен.
Или, если боишься if-а при доступе к элементам матрицы, сделай класс "транспонированная матрица", унаследованный от матрицы, имеющий внутри ссылку на исходную матрицу и оверрайдни свойство доступа к элементам.
Здравствуйте, IT, Вы писали:
IT>На бумажке уже даже не помню когда писал. Писать начинаю в коде сразу, даже ещё не понимая что получится в результате. [cut] При таком подходе опысываемые тобой ошибки исключены.
А как можно при таком подходе оценить время необходимое на реализацию ?
"Если Вы отличаетесь от меня, то это ничуть мне не вредит — Вы делаете меня богаче". Экзюпери
Здравствуйте, IT, Вы писали:
IT> Только, к сожалению, по моим наблюдениям между умением говорить такие банальности и умением их применять лежит огромная пропасть, мостиком через которую как раз и являются практические приёмы и навыки.
В самую точку.
"Если Вы отличаетесь от меня, то это ничуть мне не вредит — Вы делаете меня богаче". Экзюпери
Здравствуйте, Mirrorer, Вы писали:
IT>>На бумажке уже даже не помню когда писал. Писать начинаю в коде сразу, даже ещё не понимая что получится в результате. [cut] При таком подходе опысываемые тобой ошибки исключены.
M>А как можно при таком подходе оценить время необходимое на реализацию ?
Очень просто. Это время равно от нуля, до времени необходимого на подчистку кода.
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, Andrei N.Sobchuck, Вы писали:
ANS>То для чего можно написать юнит-тест То есть кусок кода, который можно выполнить не инициализируя всего приложения. Где-то так.
Вообще-то технология давно отлажана. Клепаешь фэйк-объекты (МОК-объекты) и оборачиваешь ими все что не лезет в концепцию юнит-тестов. Далее загружаешь нужное состояние и вперед.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[15]: Ошибок не делает тот, кто ничего не делает
Здравствуйте, Andrei N.Sobchuck, Вы писали:
AVK>>Янус спроектирован таким образом. Только проблема там в другом месте.
ANS>О да. Просвети нас, чем же именно уникален Янус?
Это скорее к тебе вопрос.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, IT, Вы писали:
IT>1. Настоящий индеец прежде всего заходит в меню Debug и в диалоге Exceptions включает галку Thrown на CLR Exceptions для managed языков. Это позволяет сэкономить не просто хучу, а туеву хучу времени при поиске ошибок. Отсюда следствие — настоящие индейцы не используют логику на исключениях, иначе весь кайф пропадает.
Жаль мы с тобой все же не всегда настоящие индейцы. Потому как после того как ты в Интеграции сам знаешь чего реализовал комбы со списками типов и методов в редактироуемом файле, то сразу стала проявляться суть настоящего индусяцкого кода лежащего в:
...\VsSDK\2007.02\VisualStudioIntegration\Common\Source\CSharp\LanguageService\CodeWindowManager.cs
// Omitting any of the following operator overloads
// violates FxCop rule: IComparableImplementationsOverrideOperators.
/// <include file='doc\CodeWindowManager.uex' path='docs/doc[@for="DropDownMember.Operator=="]/*' />public static bool operator ==(DropDownMember m1, DropDownMember m2) {
return m1.Equals(m2);
}
приводящего к ловине:
System.NullReferenceException occurred
Message="Object reference not set to an instance of an object."
Source="Microsoft.VisualStudio.Package.LanguageService"
StackTrace:
at Microsoft.VisualStudio.Package.DropDownMember.op_Equality(DropDownMember m1, DropDownMember m2)
Вот так один индус может испортить жизнь двум индейцам.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, IT, Вы писали:
FDS>>Формой с методами обработки информации.
IT>Т.е. написал для отладки отдельный гуй?
А почему нет если задача требует? Вот представь, ты должен отдать список иконок другому приложнию (студии, например). Самый прсотой способ убедиться, что с картинками все ОК — склепать примитивную форму с ЛистВью в который выведены иконки.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, IT, Вы писали:
IT>>>Кстати, один из способов, позволяющих решить эту проблему, это локальные функции. К сожалению, в C# этого нет, и пока, как я понял не планируется.
M>>Ну можно это будет лямбдами сэмулировать. Хотя для 3х уровней вложенности это ужастик получится, да.
IT>На каждую такую функцию уморишься делегаты ообъявлять. Короче, лажа это. Без крайней необходимости никто этим пользоваться не будет.
Кстати, илюстрация к грусному. По причине завязанности алгоритма на некоторые классы Студии был вынужден писать класс-хелпер на C# 2.0. Но к лямбдам и т.п. как-то прям тянет. В итоге родилось вот такое чудо:
#region MatchBraces()
private AuthoringScope MatchBraces(ParseRequest request)
{
if (!request.Sink.BraceMatching)
return GetDefaultScope(request);
// Steps:
// 1. Find token under text caret.
// 2. Determine that it is a paired token.
// 3. Determine paired token.
// 4. Find paired token in the source file.
// 5. Set info about paired tokens Sink and return it in AuthoringScope.#region Init vars
ProjectInfo projectInfo = GetProjectInfo(request);
if (projectInfo == null)
return GetDefaultScope(request);
NemerleSource source = projectInfo.GetSource(request.FileName);
IVsTextColorState colorState = source.ColorState;
Colorizer colorizer = source.GetColorizer();
NemerleScanner scanner = (NemerleScanner)colorizer.Scanner;
string lineText = source.GetLine(request.Line);
scanner.SetSource(lineText, 0);
#endregion// Steps: 1-3
BracketFinder bracketFinder = new BracketFinder(source, request.Line + 1,
request.Col + 1, scanner, colorState);
// 4. Find paired token in the source file.
ScanTokenInfo matchBraceInfo = bracketFinder.FindMatchBraceInfo();
if (matchBraceInfo != null)
{
// 5. Set info about paired tokens Sink and return it in AuthoringScope.
request.Sink.FoundMatchingBrace = true;
// Fix a bug in MPF: Correct location of left token.
// It need for correct navigation (to left side of token).
Token mactTok = matchBraceInfo.Token;
Location mactLoc = request.Reason == ParseReason.MatchBraces
&& !BracketFinder.IsOpenToken(mactTok)
? mactTok.Location.FromEnd() : mactTok.Location;
request.Sink.MatchPair( // Set tokens position info
Convert(bracketFinder.StartBraceInfo.Token.Location),
Convert(mactLoc), 0);
return new NemerleAuthoringScope(
projectInfo, request.Sink, request.FileName,
new SourceTextManager(projectInfo.GetSource(request.FileName)));
}
return GetDefaultScope(request); // we don't fing paired token
}
#endregion
#region BracketFinder class/// <summary>
/// Helper class which match paired token (brackets, brace, etc.)
/// </summary>private class BracketFinder
{
#region Fields
public ScanTokenInfo StartBraceInfo;
IVsTextColorState ColorState;
NemerleSource Source;
int StartLine;
ScanLexer Lex;
NemerleScanner Scanner;
#endregion
public BracketFinder(NemerleSource source, int startLine,
int startCol, NemerleScanner scanner, IVsTextColorState colorState)
{
#region Init fields
Scanner = scanner;
Source = source;
StartLine = startLine;
Lex = scanner.GetLexer();
Lex.SetFileName(source.GetFilePath());
ColorState = colorState;
#endregion
#region 2. Determine that it is a paired token. 3. Determine paired token.
// Get tokens of line under text carret into dynamic array.
List<ScanTokenInfo> lineToks = GetLineTokens(startLine, true);
// Find index of token which located under text carret.int index = FindIndex(lineToks,
delegate(ScanTokenInfo x)
{ return x.Token.Location.Contains(startLine, startCol); });
// If index is corret get corresponding token.
ScanTokenInfo startBraceInfo = index < 0 ? null : lineToks[index];
// Remember it, if token have paired token.if (IsPairedToken(startBraceInfo.Token))
StartBraceInfo = startBraceInfo;
else
{
// otherwise try get right-hand token...
startBraceInfo = RightHand(lineToks, index);
// Remember it, if token have paired token.if (IsPairedToken(startBraceInfo.Token))
StartBraceInfo = startBraceInfo;
}
#endregion
}
public ScanTokenInfo FindMatchBraceInfo()
{
if (StartBraceInfo == null)
return null;
// 4. Find paired token in the source file.
Token tok = StartBraceInfo.Token;
Predicate<Token> isStartBrace = GetMatchBracePredicate(tok);
Predicate<Token> isEndBrace = GetMatchBracePredicate(GetPairedToken(tok));
int nestedLevel = 1;
foreach (ScanTokenInfo tokInfo in GetTokenIter())
{
if (isEndBrace(tokInfo.Token))
nestedLevel--;
else if (isStartBrace(tokInfo.Token))
nestedLevel++;
if (nestedLevel == 0)
return tokInfo; // Match found!
}
return null; // Match not found.
}
#region GetTokenIter()
/// <summary>
/// Return iterator which allow iterate throw tokens of curent
/// source file. Iteration process start from token behind current
/// token and go forward or backward depending on type of start token.
/// If start token is open token (for example, "(" or "{") iteration
/// execute forward. If start token is close token (for example, ")"
/// or "]") iteration execute backward.
/// </summary>private IEnumerable<ScanTokenInfo> GetTokenIter()
{
bool isScanForward = IsOpenToken(StartBraceInfo.Token);
int startLine = StartLine;
// 1. Для первой строки находим токен и начиная от него сканируем вперед или назад.
List<ScanTokenInfo> tokInfs = new List<ScanTokenInfo>(
GetLineTokens(startLine, isScanForward));
int tokIndex = FindIndex(tokInfs, delegate(ScanTokenInfo ti)
{ return ti.Token.Location == StartBraceInfo.Token.Location; });
if (tokIndex < 0)
throw new Exception("tokInfs.IndexOf(startBraceInfo)");
// Scan first line from "start bracket" index.for (int i = tokIndex + 1; i < tokInfs.Count; i++)
yield return tokInfs[i];
if (isScanForward) // Scan next lines.for (int i = startLine + 1, count = Source.GetLineCount(); i <= count; i++)
foreach (ScanTokenInfo tokInf in GetLineTokens(i, true))
yield return tokInf;
else// Scan previous lines.for (int i = startLine - 1; i >= 0; i--)
foreach (ScanTokenInfo tokInf in GetLineTokens(i, false))
yield return tokInf;
}
#endregion
#region GetLineTokens()
/// <summary>
/// Get tokens of specified line.
/// </summary>
/// <param name="line">Line number which tokens it is necessary retrieve</param>
/// <param name="isForward">Direction of iteration (true is forward)</param>
/// <returns>Token list</returns>
List<ScanTokenInfo> GetLineTokens(int line, bool isForward)
{
ScanState scanState = GetLineState(line);
string lineText = Source.GetLine(line - 1);
Scanner.SetSource(lineText, 0);
Lex.SetLine(line, lineText, 0, null, null);
List<ScanTokenInfo> lst = new List<ScanTokenInfo>();
foreach (ScanTokenInfo var in GetLineTokens(Lex, scanState))
lst.Add(var.Clone());
if (!isForward)
lst.Reverse();
return lst;
}
/// <summary>Return colorer lexer state for specified line.</summary>private ScanState GetLineState(int line)
{
int state;
ErrorHandler.ThrowOnFailure(
ColorState.GetColorStateAtStartOfLine(line, out state));
return (ScanState)state;
}
/// <summary>
/// Imlementation of GetLineTokens(). Don't use this method directly!
/// </summary>
IEnumerable<ScanTokenInfo> GetLineTokens(ScanLexer lex, ScanState scanState)
{
ScanTokenInfo info = lex.GetToken(scanState);
scanState = info.State;
while (!info.IsEndOfLine)
{
yield return info;
info = lex.GetToken(scanState);
scanState = info.State;
}
}
#endregion
#region Paired token identification functions
public static bool IsOpenToken(Token token)
{
if (token is Token.BeginBrace) return true;
if (token is Token.BeginQuote) return true;
if (token is Token.BeginRound) return true;
if (token is Token.BeginSquare) return true;
if (token is Token.EndBrace) return false;
if (token is Token.EndQuote) return false;
if (token is Token.EndRound) return false;
if (token is Token.EndSquare) return false;
Token.Keyword kwd = token as Token.Keyword;
if (kwd != null)
{
if (kwd.name == "if") return true;
if (kwd.name == "else") return false;
}
throw new Exception("The token '" + token + "' not a brace!");
}
private static bool IsPairedToken(Token token)
{
if (token is Token.BeginBrace || token is Token.BeginQuote
|| token is Token.BeginRound || token is Token.BeginSquare
|| token is Token.EndBrace || token is Token.EndQuote
|| token is Token.EndRound || token is Token.EndSquare
)
return true;
Token.Keyword kwd = token as Token.Keyword;
if (kwd != null && (kwd.name == "if" || kwd.name == "else"))
return true;
return false;
}
/// <summary>
/// Return predicate function which check it argument is same token .
/// </summary>private static Predicate<Token> GetMatchBracePredicate(Token token)
{
if (token is Token.BeginBrace)
return delegate(Token t) { return t is Token.BeginBrace; };
if (token is Token.BeginQuote)
return delegate(Token t) { return t is Token.BeginQuote; };
if (token is Token.BeginRound)
return delegate(Token t) { return t is Token.BeginRound; };
if (token is Token.BeginSquare)
return delegate(Token t) { return t is Token.BeginSquare; };
if (token is Token.EndBrace)
return delegate(Token t) { return t is Token.EndBrace; };
if (token is Token.EndQuote)
return delegate(Token t) { return t is Token.EndQuote; };
if (token is Token.EndRound)
return delegate(Token t) { return t is Token.EndRound; };
if (token is Token.EndSquare)
return delegate(Token t) { return t is Token.EndSquare; };
Token.Keyword kwd = token as Token.Keyword;
if (kwd != null)
{
if (kwd.name == "if")
return delegate(Token t)
{
Token.Keyword kwd1 = t as Token.Keyword;
if (kwd1 != null)
return kwd1.name == "if";
return false;
};
if (kwd.name == "else")
return delegate(Token t)
{
Token.Keyword kwd1 = t as Token.Keyword;
if (kwd1 != null)
return kwd1.name == "else";
return false;
};
}
return null;
}
private static Token GetPairedToken(Token token)
{
if (token is Token.BeginBrace) return new Token.EndBrace(true);
if (token is Token.BeginQuote) return new Token.EndQuote();
if (token is Token.BeginRound) return new Token.EndRound();
if (token is Token.BeginSquare) return new Token.EndSquare();
if (token is Token.EndBrace) return new Token.BeginBrace(true);
if (token is Token.EndQuote) return new Token.BeginQuote();
if (token is Token.EndRound) return new Token.BeginRound();
if (token is Token.EndSquare) return new Token.BeginSquare();
Token.Keyword kwd = token as Token.Keyword;
if (kwd != null)
{
if (kwd.name == "if") return new Token.Keyword("else");
if (kwd.name == "else") return new Token.Keyword("if");
}
return null;
}
#endregion
#region Utils
static T RightHand<T>(IList<T> lst, int index) where T : new()
{
int nextIndex = index + 1;
if (nextIndex >= lst.Count)
return new T();
return lst[nextIndex];
}
private static List<T> Reverce<T>(IEnumerable<T> seq)
{
List<T> lst = new List<T>(seq);
lst.Reverse();
return lst;
}
static T Find<T>(IEnumerable<T> seq, Predicate<T> predicate) where T : class
{
foreach (T item in seq)
if (predicate(item))
return item;
return null;
}
static int FindIndex<T>(IList<T> lst, Predicate<T> predicate) where T : class
{
for (int i = 0; i < lst.Count; i++)
if (predicate(lst[i]))
return i;
return -1;
}
#endregion
}
#endregion
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, IT, Вы писали:
_M>>но по крайней мере зверя ContextSwitchDeadLock я буду включать только через свой труп — он мне столько нервов попортил...
IT>Не включай. Я лично вообще такого исключения ни разу в жизни не видел.
Это похоже то из-за чего у нас отладка под студией навертывается .
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Вообще-то технология давно отлажана. Клепаешь фэйк-объекты (МОК-объекты) и оборачиваешь ими все что не лезет в концепцию юнит-тестов. Далее загружаешь нужное состояние и вперед.
Если нечто инициализируется где-то в недрах проги, то сделать мок может не быть возможности. В том то и вопрос, что нужно писать с учетом подобных операций.
N>>В идеале, тесты должны полностью воссоздавать все окружение, в котором работает система. Например, если используется база данных, то хорошие тесты должны уметь создать ее "с нуля", прогнав необходимые скрипты, заполнить данными и "погонять" в разных режимах
_M>Такие тесты уже на юнит тесты мало похожи и если я правильно понял это называется тестированием интеграции. Сейчас пишу такой тест, получается что-то типа "виртуального пользователя", который совершает все возможные действия с программой, начиная "с чистого листа". Только результаты выполнения каждой команды сверять смысла нет, да и сложно это. Просто если во время выполнения какой-нибудь команды возникает ошибка (а в методах много проверок входных параметров, проверок промежуточных результатов), то всё это дело останавливается. Вроде нормально получается, я на правильном пути?
Почти. Результаты проверять надо. В этом как раз помогут мок-объекты.
Но для этого архитектура системы должна быть достаточно "слоистой" или хотя бы модульной.
IT>Это не всегда возможно. Точнее, это возможно для ограниченного круга задач.
На данном этапе развития технологий фактически возможно для большинства задач.
Просто конструировать надо сразу с расчётом на тестирование.
Или (в теории) на основе готовых тестов.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Re[3]: Ребят, я понимаю, что вам смешно, но проблема правда
Здравствуйте, VGn, Вы писали:
IT>>Это не всегда возможно. Точнее, это возможно для ограниченного круга задач.
VGn>На данном этапе развития технологий фактически возможно для большинства задач.
UI в это большинство задач вписывается? 30 полей на форме с более менее зависимой логикой отображения и ты состаришься раньше, чем напишешь все возможные тесты. А когда напишешь, я добавлю ещё 10 полей. Только не в конец формы, а в случайном порядке с новой особенной логикой.
VGn>Просто конструировать надо сразу с расчётом на тестирование.
Конструировать надо не в расчёте на тестирование, а в расчёте на то, шоб оно работало как хочет клиент.
VGn>Или (в теории) на основе готовых тестов.
Ну да. Главное тесты, а без функциональности клиент как-нибудь переживёт.
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
IT>UI в это большинство задач вписывается? 30 полей на форме с более менее зависимой логикой отображения и ты состаришься раньше, чем напишешь все возможные тесты. А когда напишешь, я добавлю ещё 10 полей. Только не в конец формы, а в случайном порядке с новой особенной логикой.
Утверждалась возможность, а не простота реализации, что тоже в конечном счёте вопрос инструментов.
VGn>>Просто конструировать надо сразу с расчётом на тестирование. IT>Конструировать надо не в расчёте на тестирование, а в расчёте на то, шоб оно работало как хочет клиент.
Нет. Так говорят малограмотные ПМы.
Это требования формулировать необходимо на основе пожеланий клиента.
Тесты — на основе требований.
А код — удовлетворяющий этим тестам, а соответственно и всему остальному в обратном порядке.
Тогда мы всегда сможем трассировать пожелания-требования-тесты-код при изменении любой составляющей.
И в конечном счёте не будет обычного бардака с требованиями и реализацией.
VGn>>Или (в теории) на основе готовых тестов. IT>Ну да. Главное тесты, а без функциональности клиент как-нибудь переживёт.
Если функциональность будет присутствовать только номинально, клиенту это тоже врядли понравится
(Кстати сейчас это — основная болезнь в нашей компании. Потому что не смотря на CMMI4 адекватного процесса разработки нет. А на самом деле это не такой уж тяжкий труд. Просто каждый на своём месте вместо постоянного п****ежа и пустых совещаний должен просто правильно делать свою работу. Конечно, обладая для этого соответствующей квалификацией.).
Здравствуйте, VGn, Вы писали:
IT>>UI в это большинство задач вписывается? 30 полей на форме с более менее зависимой логикой отображения и ты состаришься раньше, чем напишешь все возможные тесты. А когда напишешь, я добавлю ещё 10 полей. Только не в конец формы, а в случайном порядке с новой особенной логикой.
VGn>Утверждалась возможность, а не простота реализации, что тоже в конечном счёте вопрос инструментов.
Это как? В принципе сделать можно, но сложность не позволяет?
VGn>>>Просто конструировать надо сразу с расчётом на тестирование. IT>>Конструировать надо не в расчёте на тестирование, а в расчёте на то, шоб оно работало как хочет клиент.
VGn>Нет. Так говорят малограмотные ПМы.
ПМы могут говорить что угодно. Но в данном случае обратное обычно утверждают юные начинающие архитекторы.
VGn>Это требования формулировать необходимо на основе пожеланий клиента. VGn>Тесты — на основе требований.
Т.е. главное у нас всегда требования. А тесты — это всего лишь побочный эффект попытки добиться приемлемой сопровождаемости системы.
VGn>А код — удовлетворяющий этим тестам, а соответственно и всему остальному в обратном порядке. VGn>Тогда мы всегда сможем трассировать пожелания-требования-тесты-код при изменении любой составляющей. VGn>И в конечном счёте не будет обычного бардака с требованиями и реализацией.
Не понял как это связано. Когда меняются требования, то меняется код, а следовательно меняются и тесты. На бардак с требованиями это никак не влияет.
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
IT>Это как? В принципе сделать можно, но сложность не позволяет?
Сложность — не абсолютная величина. Она в большинстве случаев зависит от подхода.
VGn>>>>Просто конструировать надо сразу с расчётом на тестирование. IT>>>Конструировать надо не в расчёте на тестирование, а в расчёте на то, шоб оно работало как хочет клиент. VGn>>Нет. Так говорят малограмотные ПМы. IT>ПМы могут говорить что угодно. Но в данном случае обратное обычно утверждают юные начинающие архитекторы.
В том числе Макконел и Кент Бек и другие
IT>Т.е. главное у нас всегда требования. А тесты — это всего лишь побочный эффект попытки добиться приемлемой сопровождаемости системы.
Главное — безусловно, требования. А тесты — инструмент их обеспечения.
IT>Не понял как это связано. Когда меняются требования, то меняется код, а следовательно меняются и тесты. На бардак с требованиями это никак не влияет.
Неужели? И всегда без тестов легко можно 100% трассировать требования напрямую в код?
С этим справится любой аналитик или пм?
Здравствуйте, VGn, Вы писали:
IT>>Это как? В принципе сделать можно, но сложность не позволяет? VGn>Сложность — не абсолютная величина. Она в большинстве случаев зависит от подхода.
Мы как раз и обсуждаем конкретный подход, подход принятый в большинстве систем построения UI.
VGn>>>>>Просто конструировать надо сразу с расчётом на тестирование. IT>>>>Конструировать надо не в расчёте на тестирование, а в расчёте на то, шоб оно работало как хочет клиент. VGn>>>Нет. Так говорят малограмотные ПМы. IT>>ПМы могут говорить что угодно. Но в данном случае обратное обычно утверждают юные начинающие архитекторы. VGn>В том числе Макконел и Кент Бек и другие
Они утверждают, что тесты первичны, а требования вторичны? Можно ссылочку на эти утверждения?
IT>>Т.е. главное у нас всегда требования. А тесты — это всего лишь побочный эффект попытки добиться приемлемой сопровождаемости системы. VGn>Главное — безусловно, требования. А тесты — инструмент их обеспечения.
Требования обеспечиваются кодом, а не тестами.
IT>>Не понял как это связано. Когда меняются требования, то меняется код, а следовательно меняются и тесты. На бардак с требованиями это никак не влияет. VGn>Неужели? И всегда без тестов легко можно 100% трассировать требования напрямую в код?
Я не знаю что такое трассировать требования в код.
VGn>С этим справится любой аналитик или пм?
А они то тут при чём?
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
IT>Мы как раз и обсуждаем конкретный подход, подход принятый в большинстве систем построения UI.
Кем принятый?
IT>Они утверждают, что тесты первичны, а требования вторичны? Можно ссылочку на эти утверждения?
Противопоставление здесь не уместно.
IT>Требования обеспечиваются кодом, а не тестами.
Близорукий подход.
IT>Я не знаю что такое трассировать требования в код.
Очень жаль.
Повышайте свою квалификацию.
IT>А они то тут при чём?
Управлением требованиями у нас занимается только разработчик?
Здравствуйте, VGn, Вы писали:
IT>>Мы как раз и обсуждаем конкретный подход, подход принятый в большинстве систем построения UI. VGn>Кем принятый?
Производителями UI библиотек.
IT>>Они утверждают, что тесты первичны, а требования вторичны? Можно ссылочку на эти утверждения? VGn>Противопоставление здесь не уместно.
Вполне уместно. Это получается само собой при расстановки приоритетов для принятия решений.
IT>>Требования обеспечиваются кодом, а не тестами. VGn>Близорукий подход.
Очень аргументировано. А ставить тесты во главу угла — это дальнорукий подход.
IT>>Я не знаю что такое трассировать требования в код. VGn>Очень жаль. VGn>Повышайте свою квалификацию.
Моей квалификации достаточно, чтобы за чушью из от фонаря сочинённых терминов распознать банальнейшую подмену приоритетов.
ЗЫ. Следущая попытка обсуждения квалификации собеседника будет квалифицирована как переход на личности со всеми вытекающими. IT.
IT>>А они то тут при чём? VGn>Управлением требованиями у нас занимается только разработчик?
Реализаций требований занимается разработчик.
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, VGn, Вы писали:
AVK>>Есть. Но программистов АЭС подавляющее меньшинство, это не мейнстрим.
VGn>И в обычных системах есть какое-то критическое количество ошибок, за которое дают по башке.
Дают. Но значительно больше по башке дают за прожранные бабки и сроки и неготовый продукт.
... << RSDN@Home 1.2.0 alpha rev. 675 on Windows Vista 6.0.6000.0>>