Блог им. Replikant_mih
Поделитесь как дебажите код? — вернее, как проверяете на корректность работы в целом — ну типа каждый блок, каждый кусок кода отдельно и сразу при написании, или не сразу но отдельно, если не сразу, то когда? — не важно, стратегию ли кодите или софт.
Как у меня сейчас — пишу длинный код, потом пробую запустить, потом исправляю ошибки, которые мешают компилироваться)), потом ищу почему код вообще ничего не делает)), следующим этапом ищу, почему код что-то уже делает, но что-то какое-то совсем не то, что я от него ожидаю)). Может быть правильней будет каждый кусок кода отдельно и сразу тестить, в таком режиме мне мешает работать, видимо, подсознательная вера в то, что длинный код возьмет и сразу запустится без ошибок — к слову, такое бывает крайне редко.
Иногда практикую первый проход новой задачи делать в режиме пошагового исполнения с контролем значений, но, как правило, предпочитаю внимательно вычитывать код несколько раз (больше двух) — перерасход затраченного времени все равно окупается против ситуаций, когда просто пытаешься выявить, что же работает не так.
Раньше пробовал новые блоки тестить примерами, но это слишком долго, мне лень. Проще дать коду отлежаться длительное время, потом со свежим взглядом еще раз все осмотреть.
Ну а рантайм ошибки — быстренько глянуть код, если не обнаружил причину сразу — то только пошагово до победного конца.
тестирую в демо квике
написал часть — запустил — проверил-пишем дальше
практически к каждой строке коментарий
запись в файл выполнение каждой функции, максимум записей
пишу на луа для квика, вернее пытаюсь)
Replikant_mih, ну в чужой программе вообще тяжело разобраться (кстати коменты очень бы помогли), а если она написана коряво — то вообще нереально
думаю многие согласятся что написать свою легче чем разобраться в чужой)
Igr, Комменты пишу, помогает «возвращаться» в код после перерыва.
В своем конечно легче копаться — ну хотя если чужой написан профессионально и стройно, а свой коряво — то может проще будет в чужом))
Replikant_mih, ага, я только ради возвращения и пишу)
если к каждой строчке будет комент — то вполне возможно
Михаил, согласен, я далеко не профи
но я например пишу комент не только к своим функциям, но и к функциям обратного вызова, т.к. тупо забываю что именно какая делает, т.е. это не относится с качеству кода
Все зависит от языка, где есть функции первого класса — переприсвойте плохое имя сторонней функции более вразумительному. Если такого синтекса в языке нет, то просто оберните вызов сторонней функции вызовом функции с понятным для вас именем. Или поставьте нормальную среду разработки, чтобы в один клик смотреть хлеп по сторонним функциям.
Михаил, пока не забывал
функция срабатывает, например, при изменении в стакане, её не переименовать, вот к ней комент и пишу
в Notepad++ пишу, не вижу разницы, что комент что хелп смотреть, с хелпом то наверное легче забыть подправить когда меняешь кода
Code complete
Clean code
Refactoring: Improving the Design of Existing Code
Я стараюсь проверять по частям.
Хотя конечно часто бывает лень или нет сил. Но лучше проверять по частям. И потом всё вместе.
Потому что если жопа может стрястись, она всегда стрясается.
В последнее время ошибки нахожу по принципу — «что-то тут такое странное какое-то, то так то сяк», а не «тут же блин вообще всё должно быть не так, почему оно так?»
ещё помогает на бумажечке всё продумать и описать словами.
потом сделать.
ошибки будут всё равно.
ПBМ, да, тема видимо, меня цепляет — поэтому не первый пост)).
Многие баги у меня чисто на лени, с массивом, допустим, работаю — написал код, думаю: а здесь надо > или >= чтоб все элементы пройти и за границы не выйти?? — аа, ладно, так сойдет, эксепшен выпадет — починю)), а похорошему надо бы досконально все такие моменты сразу прорабатывать.
Ну и да, на бумажке это штука полезная!
Replikant_mih, сойдёт?!? не, это не наш метод)
потом проблем может быть куда как больше, в разы из-за этой мелочи вроде как
Replikant_mih, да, однотипность, единый принцип написания, названия, всех функций хорошее дело, облегчает дальнейшую работу
на основе старой проги можно написать что то новое, то есть использовать однотипные функции
1. Делю задачи, которые код должен выполнить, на функции. Если сильно надо, то пишу класс или библиотеку (редкое явление).
2. Запускаю и дебажу отдельные функции.
3. Когда они работают нормально, как ожидается, то уже складываю их в последовательность.
Преимущества — дробится код, легко понять где что не работает. Не происходят такие события, когда жмешь PLAY и на экране вуду.
Недостатки — погружаясь в решение одной функции, смывается горизонт общей задачи.
Баланс и решение — ДРАКОН (гуглите, мощная тема). Сначала строю блок-схему кода, а потом его пишу. Благодаря процедурной логике ДРАКОНа сложно ошибиться в реализации. А уж если ошибки всплывают, то не нужно рефакторить весь код. Всплывают баги конкретные в конкретных местах. Таким образом экономится время, и разработка становится делом техники, а не творчества.
Успехов! )
facevalue, Интересные идеи, спасибо, обдумаю.
Не знаю, как насчет дракона, а вот больше дробить функции и кажду отдельную проверять на корректность — такое, скорее всего, возьму на вооружение.
>> «разработка становится делом техники, а не творчества»
да, наверное, хотелось бы к такому прийти)
Каждую функцию отдельно и сразу при написании. Пока мысли по этой функции сидят в голове в «оперативной памяти», а не от'swap'ились на диск.
Проверять следует для разных наборов входных параметров, — таких, чтобы функция, в результате, прошла по всем своим веткам. Следует убедиться, что ветвление происходит правильно, и каждая ветка работает, как задумано.
Следует проверить работоспособность функции при крайних значениях входных параметров и при значениях около качественных переходов (например, от отрицательных значений к положительным, проверить, существуют ли ситуации, когда возникает деление на 0 и так далее).
Код самой функции также полезно подвергнуть анализу на тему — могут ли возникнуть бесконечные циклы и тому подобные безобразия.
Также неплохо проверить дизайн функции на наборы «нехороших» сочетаний значений входных параметров, например противоречивых, не имеющих смысла. Если таковые обнаруживаются, следует передизайнить функцию. Как минимум хотя бы на входе обработать эти ситуации и вернуть ошибку, если обнаруживается «нехорошее» сочетание.
Вообще, до 90% программирования — обработка ошибочных ситуаций (запрограммирование правильной реакции на них).
Лениться нельзя. Можно, разве, что, в качестве баловства. В программировании «лень» не прощается. Это как раз один из моментов, когда программирование положительно влияет на индивидуума.
А тесты не только полезны, но и вредны. Причём, вредны больше, чем полезны, ибо, с одной стороны, провоцируют ту же самую лень (ведь, кажется, что тесты отловят ошибку, если что, но это только так кажется), а, с другой стороны, покрытие тестами, как правило, весьма слабо, ибо доскональные тесты писать значительно ленивее, чем просто как следует отладить код.
Какие-то основные, базовые тесты иметь может оказаться полезным. Но полагаться полностью на них нельзя, их можно использовать только для грубой проверки работоспособности кода в целом.
Unworldly, Хороший комментарий не грех и анонсировать)). Мне нравится ваш подход — основательный — про ветвления, предельные значения и аналогичное — запомню.
Ну и идеи про то, что такой подход может привить правильную дисциплину — как трейдинг меня поменял — знаю, а сейчас осознал, что и программирование может).
Каждая функция должна заниматься одним делом, выполнять только одну задачу. Если в функции делается несколько дел одновременно, такую функцию следует разбить на несколько.
Также функции должны, в основном, образовывать иерархическую структуру. Самые низкоуровневые функции, которые, в основном, обходятся самим языком и обращениями к системе, потом функции более верхнего уровня, которые используют низкоуровневые функции, далее, функции третьего уровня, которые используют для своей реализации функции второго уровня и так далее. «Перепрыгивать» через уровень следует стараться избегать (дизайн всей программы — не очень, если так не получается).
Ну, и, опять же, функция каждого уровня должна решать только одну задачу, это универсальное правило, от уровня не зависит.
Replikant_mih, в целом, да.
Честный ООП требует отдельного типа мышления, это бывает непросто даже для программистов. ООП незаменим для борьбы с колоссальной сложностью. Но процедурного или «почти процедурного» подхода вполне достаточно для того уровня сложности, с которым обычно приходится иметь дело.
Если хочется более явно разграничить, можно использовать namespace'ы, если они есть в используемом языке. Классы в режиме namespace'ов тоже можно использовать, но это искусственно, уж лучше соответствующее именование, типа использования «префиксов» в именах.
Тут, скорее, модель «иерархии библиотек» в качестве упрощённого ООП подойдёт лучше. Каждая «библиотека» предоставляет набор функций (сервисов), или API. «Библиотека», которая находится над ней, пользуется этим набором сервисов, чтобы предоставить уже свой набор функций более высокого уровня и так далее. «Библиотека» может использовать не одну, а несколько «библиотек» уровнем ниже. Получается такой упрощённый ООП, это значительно проще с точки зрения обычного мышления.
Каждую «библиотеку» можно отладить отдельно, и это бывает удобно, поскольку каждая такая «библиотека», как правило, содержит набор логически связанных функций.
Разбивку исходной задачи на условные «библиотеки» можно выполнить сверху вниз: сначала главную задачу разбить на подзадачи. Эти подзадачи будут решаться функциями «библиотеки» (или нескольких «библиотек», если подзадачи явно распадаются на несколько разнородных групп) верхнего уровня. Далее, каждую подзадачу, в свою очередь также разбить на подзадачи… пока подзадачи не станут достаточно простыми, чтобы решаться с помощью функции, решающей одну простую задачу. Примерно таков алгоритм построения иерархии (сверху вниз).
Этот процесс многопроходный и итерационный, поскольку по мере разбивки начинают выясняться детали, которые были сразу не ясны на верхнем уровне, и которые требуют корректировки (а то и смены) дизайна программы. Но процесс полезный, поскольку разработчик, проходя его, начинает значительно лучше понимать, что и как будет происходить в разрабатываемой им программе.
Во время процесса разбивки также следует постоянно помнить и учитывать возможность возникновения ошибочных ситуаций и дизайнить их обработку тоже. Обработка ошибочных ситуаций должна быть запрограммирована явно.
Кстати, учёт ошибочных ситуаций может довольно сильно повлиять на дизайн всей программы…
Разбивка получается сверху вниз, а реализация и отладка — в обратном порядке.
Unworldly, спасибо за подробный ответ! :) И предыдущие тоже).
C# — первый и единственный язык, который более или менее серьезно изучаю, поэтому хочется верить, что ООП я впитываю нативно). Namespae'ы их иерархия и в целом использование для организации это да, но все-таки мне важнее классы — я их активно использую, т.е. мне интересно как в классе все инкапсулируется и как в классе организовать иерархию.
По моим умозрительным выкладкам, правильная иерархия — делать часть полей типами классов (или как там правильней по говорить)) ) — т.е. если в классе 20 полей и ты понимаешь, что 5 из них описывают какую-то дополнительную сущность — вычленять в отдельный класс и будет 15 полей и 1 поле — объект класса. Соответственно, наверно, и методы этого объекта из 5-ти полей можно в нем инкапсулировать.
Допустим мы эти 20 полей разобьем на 3 класса и 5 полей простых типов данных — это уже мы внутренние простые логики этих 3-х классов засунем в эти классы, соответственно в самом объекте можно оставить только логики (функции) взаимодействия между этими объектами-полями — уже поинтересней, вроде). А вот классы (количество которых при таком подходе будет большим) уже можно иерархично раскидывать по нэймспэйсам.
Что-то интересное выкристализовывается).
Классы по namespace'ам вряд ли придётся распихивать, достаточно самих классов.
Но с классами всё сложнее. Там не только агрегирование возможно, но и наследование. Опять же, доступ к полям/методам (private, protected, public), конструкция/деструкция, а раз — классы, то обязательно — исключения… Код должен быть безопасен с точки зрения возникновения исключений… И так далее. Стоит ли оно того, чтобы всем этим и не только этим овладевать для решения своих задач?
C# не знаю вообще, принципиально не доверяю Microsoft. И в C# могут быть свои дополнительные приколы.
Программирование — огромно, оно уже переплюнуло медицину по объёму и сложности.
В программировании нужно понимание.
Zweroboi, у кого-то арсенал методов включает в себя методы с фамилиями через дефис в названиях), а у меня будут вида:
— метод пристального взгляда.
— метод непредвзятого тыка...
)))
tranquility, Да, стараюсь ООП задействовать — пока, правда, затрудняюсь оценить, насколько это у меня получается)).
Да, алготрейдинг не для ленивых), мои предыдущие виды деятельности меня этому не готовили))).
в Квике на ЛУА пошагового исполнения капец как не хватает ((
а может я какого инструмента не знаю, чтоб пошагово можно было дебажить… У кого что есть на этот счет ?
Я логами в DebugView ошибки ловлю.
Но самый капец конечно когда в простейшей функции ошибка выскакивает, типа получил Nil, а ждал стринг или инт, а функцию эту вызываешь из разных мест и вот вычислить то место которое этот nil отправило засада блин.
вставляя в код точки пишущие в файл
и в файле читал логику на понятном языке
буквально: «если больше тогда перешли в цикл»
типа того
запись в файл реализуема хоть на qbasic
и вообще щаз вспоминаю:
у меня 99% программ пишут в файл
Таким образом, каждая новая разработка сводится к дебагингу только новых кусков кода, которые составляют лишь несколько процентов всей программы. А как именно отлаживать эти маленькие кусочки — каждый выбирает сам. Главное чтобы самому удобно было.
Научитесь декомпозировать, но при этом не усложнять.
Изучите базовые строительные блоки(паттерны).
Для начала достаточно создавать отдельные классы для разных бизнес сущностей.
По возможности тестируйте блоки по отдельности.
Затем в связках, чтобы выловить проблемы взаимодействия.