рефакторинг с тестами или без? [re: unit-тесты для БД]
без автотестов рефакторинг нельзя делать?Зависит от того, что ты подразумеваешь под этим словом. Я перемалываю более 100% всего написанного кода, это нереально без юнит тестов.
Зависит от того, что ты подразумеваешь под этим словом.это да. Я могу рефакторить два дня и не внести ни одной баги. Без автотестов.
Без автотестов.точнее даже вообще ни разу не побывав в рантайме.
Я могу рефакторить два дня и не внести ни одной баги.Это ни о чём не говорит. Ты либо внесёшь её на третий день, либо потратишь кучу времени на регрессионное тестирование.
ну ... я наверное немного загибать стал но для иллюстрации своей позиции сойдет и так
ы либо внесёшь её на третий день,нет, не внесу.
либо потратишь кучу времени на регрессионное тестирование
зачем я буду тратить много времени, если я уверен за свой стиль работы с кодом?
нет, не внесу.Либо внесёшь, либо делаешь что-то тривиальное и/или 100% автоматизированое.
зачем я буду тратить много времени, если я уверен за свой стиль работы с кодом?За тем, что без тестирования код без ошибок можно написать только с огромными временными затратами. Уж это-то известно любому практикующему программисту, даже если он никогда не писал автотестов.
Либо внесёшь, либо делаешь что-то тривиальное и/или 100% автоматизированое.нетривиальное. процент автоматизации близок 100%. В этом и фишка, что в C# рефакторинг почти весь автоматизирован.
За тем, что без тестирования код без ошибок можно написать только с огромными временными затратами. Уж это-то известно любому практикующему программисту, даже если он никогда не писал автотестов.
Во-первых, мы сейчас про рефакториг говорим, а не про написание.
Во-вторых, код без ошибок написать нельзя в любом случае; в любом коде есть ошибки. Любой практикующий программист знает, что код без ошибок — понятие растяжимое.
В этом и фишка, что в C# рефакторинг почти весь автоматизирован.Я же в самом начале спросил, что ты понимаешь под рефакторингом. Если только набор самых тривиальных паттернов из ReSharper-а, которые делаются автоматически, то да, тут ошибиться сложно. Правда мне не до конца понятно, зачем на это тратить целых два дня. Если же мы говорим про что-то сложное, то более чем очевидно, что без тестирования ты наделаешь кучу ошибок.
Во-первых, мы сейчас про рефакториг говорим, а не про написание.
Я уже написал, что в моём понимании означает нетривиальный рефакторинг. Для примера: замена switch/case на State, замена ручной проверки типов на Visitor, расслоение класса для восстановления SRP, и так далее. Без тестов даже extract method с последующей полировкой может внести ошибку в код.
Я уже написал, что в моём понимании означает нетривиальный рефакторинг.может я что-то пропустил, но ты написал только "Я перемалываю более 100% всего написанного кода". Это не тянет на пояснение понятия.
замена switch/case на State
где здесь можно ошибиться? вероятность ошибки крайне мала. Аккуратненько переносишь кусочки кода, да и всё. Ну это при условии, если сам switch был грамотно написан. А если не грамотно, то это уже не рефакторинг, а переписывание говнокода.
замена ручной проверки типов на Visitorтоже самое
расслоение класса для восстановления SRPтут тоже самое, только еще проще, только кнопки в ReSharper-е нажимай
Без тестов даже extract method с последующей полировкой может внести ошибку в код.
Это как?
В общем “большой” рефакторинг разбивается на мелкие шажки, в которых практически сложно ошибиться. Просто соблюдаешь эту процедуру и не делаешь лишних телодвижений. Ну еще я часто чикинюсь (желательно в feature branch).
Аккуратненько переносишь кусочки кода, да и всё.
И либо тратишь много времени, либо делаешь ошибку.
тут тоже самое, только еще проще, только кнопки в ReSharper-е нажимай
Тут на порядки сложнее, и кнопок в ReSharper-е для этого, конечно же, не хватает.
В общем “большой” рефакторинг разбивается на мелкие шажки, в которых практически сложно ошибиться. Просто соблюдаешь эту процедуру и не делаешь лишних телодвижений.Точно так же выглядел бы аргумент про программирование в целом: большая программа просто разбивается на мелкие шажки, в которых практически сложно ошибиться. Просто соблюдаешь эту процедуру и пишешь код без ошибок.
"Я перемалываю более 100% всего написанного кода". Это не тянет на пояснение понятия.Это тянет на пояснение сложности процесса: очевидно, достичь такой цифры простыми автоматизированными процедурами нельзя; да я и не включаю их в эти 100%.
Для примера: замена switch/case на State, замена ручной проверки типов на Visitor, расслоение класса для восстановления SRPПока складывается впечатление, что юнит тесты нужны:
любителям динамических языков;
ООП-шникам;
работникам с говнокодом.
Точно так же выглядел бы аргумент про программирование в целом: большая программа просто разбивается на мелкие шажки, в которых практически сложно ошибиться. Просто соблюдаешь эту процедуру и пишешь код без ошибок.Про таких творческих рефакторщиков patnic правильно написал:
а он прав. я бы вобще всех рефакторщиков кормил бы бумагой из шредера.
Немного провокацийНемного провокаций.
Когда я вижу очередной пост от Шурика, где он пишет какие-то умные слова, у меня складывается впечатление, что он жираф. Потому что в каждом таком посте он уже сделал свой вывод, и не важно, ошибочный этот вывод или нет. Всё равно его никто не сможет переубедить, потому что у него длинная шея и ему виднее.
В самом деле, в данной теме на реальные практические аргументы до сих пор был только один ответ, который подразумевает, что Шурик очень-очень круто кодит и при этом не делает ошибок. С этим можно было бы согласиться, если бы он при этом признавал, что всё время тестирует результат этого кодирования. Но оказывается, он может делать рефакторинг два дня подряд без тестирования и не сделать ни одной ошибки. Единственный вывод, который я могу из этого сделать, заключается в том, что под рефакторингом понимается автоматическое переименование классов, методов и полей.
при рефакторинге есть такая штука, что правильность каждого изменения можно доказать - доказав, что данное маленькое изменение не меняет поведение всей программы.
зы
имхо, тестирование помогает только от замыления глаза (глупые описки, опечатки и т.д.) + системное тестирование помогает избежать ситуации: когда представление разработчика не совпадает с реальным миром.
всё остальное лучше делать через доказательство правильности работы кода.
доказательство правильности работы кода всегда надежнее работает, чем тестированиеИ поэтому нигде на практике не реализовано хоть сколько-нибудь сложной верификации. Мы это уже обсуждали, не вижу смысла опять поднимать эту тему, если она практически непригодна.
при рефакторинге есть такая штука, что правильность каждого изменения можно доказать - доказав, что данное маленькое изменение не меняет поведение всей программы.Во-первых, это никак не спасёт тебя от появления ошибок в коде: маленькое изменение можно сделать неправильно, если оно не автоматизируется. Во-вторых, я привёл пример рефакторинга, который ты либо практически не верифицируешь, либо не выполнишь маленькими шагами за разумное время.
имхо, тестирование помогает только от замыления глаза (глупые описки, опечатки и т.д.) + системное тестирование помогает избежать ситуации: когда представление разработчика не совпадает с реальным миром.Тестирование помогает находить реальные ошибки, быстрее разрабатывать код, выполнять рефакторинг, избегать регрессии (одинаковый по функциональному эффекту баг может быть вызван разными причинами).
всё остальное лучше делать через доказательство правильности работы кода.
Всё остальное лучше делать. Доказать правильность работы каждой строчки кода в любом сложном проекте практически нереально, хотя бы из-за временных затрат. Да и необходимость в этом возникает разве что в предельно узких сферах разработки ПО: в GUI какого-нибудь бэкофиса верификация никому не всралась, а вот юнит тесты позволят быстрее разрабатывать и дорабатывать проект, и повысят его качество.
Шурик очень-очень круто кодит и при этом не делает ошибок. С этим можно было бы согласиться, если бы он при этом признавал, что всё время тестирует результат этого кодирования.Эээ, как бы "очень круто кодит" и тратит много времени на тестирование это как раз противоположности!
Видимо, ты не в курсе что такое "очень круто кодит".
Эээ, как бы "очень круто кодит" и тратит много времени на тестирование это как раз противоположности!Ты не прочитал часть фразы "не делает ошибок".
если ты про автоматизированную - то да, это достаточно дорогая и сложная штука, и применяется только где нужна сверх надежность.
но доказательство правильности работы кода человеком применяется сплошь и рядом.
например, правильность написания многопоточных приложений почти невозможно протестировать.
но можно доказать, что во-первых, все переменные доступные из нескольких тредов защищены локами, а во-вторых - что локи не могут попасть в dead-lock.
конечно, при этом используются не строгие формы доказательств, и может даже правильно это называть словом "показывается".
т.е. разработчик показывает сам себе (и окружающим) почему он считает, что его код будет работать правильно всегда.
зы
если брать "хороший запах кода", то он тоже в большинстве своем направлен на то, чтобы упростить "показывание" того, что код будет работать правильно.
на это же направлено и Code Review.
> Тестирование помогает находить реальные ошибки, быстрее разрабатывать код, выполнять рефакторинг, избегать регрессии (одинаковый по функциональному эффекту баг может быть вызван разными причинами).
согласен, и я не против тестирования.
я лишь хочу отметить, что тестирование имеет смысл применять совместно с доказыванием (показыванием) что код будет работать правильно.
> Доказать правильность работы каждой строчки кода в любом сложном проекте практически нереально, хотя бы из-за временных затрат
а это и не надо.
локальная опечатка быстро себя проявит на тех же системных или регрессионных тестах.
доказывать необходимо правильность взаимодействия нескольких участков куда
например, правильность написания многопоточных приложений почти невозможно протестировать.впомнился мне случай на собеседовании, где попросили решить многопоточную задачку
но можно доказать, что во-первых, все переменные доступные из нескольких тредов защищены локами, а во-вторых - что локи не могут попасть в dead-lock.
решение на листочке. закончив писать, я заикнулся, что все равно мы с вами не сможем проверить корректность этого кода, потому что многопоточные приложение очень сложны
мне не поверили, и мы втроем (я + два интервьюера) убили минут 40, чтобы убедиться, что алгоритм будет правильно себя вести в многопоточном окружении (я до сих пор не уверен по поводу правильности)
но доказательство правильности работы кода человеком применяется сплошь и рядом.Это странный аргумент в пользу того, что "доказательство правильности работы работает куда надёжнее, чем тестирование". Если после каждой итерации рефакторинга сидеть и доказывать, что у тебя в очередной раз получился правильный код, то на это будет уходить слишком много времени. Юнит тесты позволяют сократить это время в десятки раз.
я лишь хочу отметить, что тестирование имеет смысл применять совместно с доказыванием (показыванием) что код будет работать правильно.Тестирование вообще имеет смысл применять. В рамках этой темы нет разницы, с чем это делать.
впомнился мне случай на собеседовании, где попросили решить многопоточную задачкуЧто за задачка?
Если после каждой итерации рефакторинга сидеть и доказывать, что у тебя в очередной раз получился правильный код, то на это будет уходить слишком много временичем этот аргумент отличается от: если после каждого изменения функциональности кода писать тесты, то на это будет уходить слишком много времени?
зы
просто ты не пробовал (и не учился) показывать правильность работы кода, поэтому тебе и кажется что это занимает много времени.
чем этот аргумент отличается от: если после каждого изменения функциональности кода писать тесты, то на это будет уходить слишком много времени?Этот аргумент отличается тем, что при изменении кода не обязательно меняется функциональность, поэтому тесты можно не трогать. Так же нужно понимать, что даже если разработчикам приходится менять тесты, на это всё равно уходит в десятки раз меньше времеми, чем занимает повторная компиляция и интерпретация кода у себя в голове.
просто ты не пробовал (и не учился) показывать правильность работы кода, поэтому тебе и кажется что это занимает много времениЭто вообще какой-то странный наезд, который содержит неизвестно откуда сделанный вывод; и, кроме всего прочего, каким-то непонятным образом связан с обсуждаемой темой.
Давай возьмём простую практическую задачу: нужно разделить на части класс, в котором нарушен SRP. Если методы этого класса уже покрыты тестами, то эта задача решается очень быстро и с минимальным риском возникновения ошибки. Если тестов нет, то я гарантирую две вещи. Во-первых, на проверку уйдёт намного больше времени (в частности и потому, что тесты подтверждают скомпилированную и проинтерпретированную тобой в голове картину). Во-вторых, риск возникновения ошибки будет в несколько раз выше.
ты, наверное, настолько крутой, что с тобой в команде никто работать не хочет?
ты, наверное, настолько крутой, что с тобой в команде никто работать не хочет?ой, как это? вроде ж наоборот, нормальные люди как раз стараются найти возможность поработать с крутыми, нет?
*шутка про int overflow*
Соответственно, если использовать изменяемое состояние по минимуму, то с большой долью вероятности рефакторингу не потребуется специальные тесты.
А какой вид рефакторинга SRP или еще какая хрень это не важно, если выполняется условие неизменности изменяемого состояния.
Если при рефакторинге мы не меняем изменяемые состояния (забавный каламбурчик то такой рефакторинг легко разбивается на маленькие шаги, которые легко сделать без ошибок.Ключевой момент заключается в том, что "легко сделать без ошибок" не означает "всегда делается без ошибок".
А какой вид рефакторинга SRP или еще какая хрень это не важно, если выполняется условие неизменности изменяемого состояния.
При чём тут состояние: ты запросто можешь сделать ошибку в control flow.
"легко сделать без ошибок" не означает "всегда делается без ошибок".я ж уже писал, что ошибки есть всегда и везде. Вопрос лишь в том какое их количество допустимо в конкретном проекте.
При чём тут состояние: ты запросто можешь сделать ошибку в control flow.
не представляю каким образом это можно сделать. Всё ж почти к последовательности ReSharper-овских кнопок сводится.
Вопрос лишь в том какое их количество допустимо в конкретном проекте.Я без понятия, сколько их допустимо в твоих проектах; но в моих я стараюсь свести это количество к нулю. И автотесты этому, очевидно, очень хорошо помогают.
не представляю каким образом это можно сделать. Всё ж почти к последовательности extract method/parameter сводится.Я ещё раз повторю: кроме автоматического рефакторинга есть ещё куча всяких вещей, которые посложнее. Тебе что, действительно не приходит в голову мысль, что при расслоении класса методы оригинала могут сильно изменяться? Или что в двух методах может быть общая часть, которую захочется выделить? Или ещё куча всего, что не связано с автоматизированными паттернами в ReSharper?
Тебе что, действительно не приходит в голову мысль, что при расслоении класса методы оригинала могут сильно изменяться?да пожалуйста меняй как хочешь, только вот я не могу представить ситуацию, когда это всё нельзя свести к последовательности (иногда немаленькой) авто-шажков.
Или что в двух методах может быть общая часть, которую захочется выделить?
общий кусок выдели в метод, в чем проблема? Уже сейчас решарпер может при выноси параметра делать лямбды. Я писал им на форум, обещали сделать еще более общую форму такого рефакторинга. Детали могу вспомнить, если надо.
Или ещё куча всего, что не связано с автоматизированными паттернами в ReSharper?
Ну кое что можно ему и подсказать. Например, если выносишь кусок метода как параметр в виде лямбды, то сначала делаешь руками эту лямбду локальной переменной (ошибиться сложно). А потом жмешь вынести параметр.
да пожалуйста меняй как хочешь, только вот я не могу представить ситуацию, когда это всё нельзя свести к последовательности (иногда немаленькой) авто-шажков.Про это мы уже говорили, и я предложил тебе сразу писать программы маленькими шажками и без ошибок. Ты уверен, что читаешь то, что тебе пишут?
общий кусок выдели в метод, в чем проблема? Уже сейчас решарпер может при выноси параметра делать лямбды
Да уж, я не думал, что автоматизация программирования так сильно развращает... А ты никогда не замечал общее в двух методах, когда это общее можно выделить только с изменением control flow? А про то, что при расслоении классов в каком-то методе не будут доступны все поля, используемые ранее?
Ну кое что можно ему и подсказать. Например, если выносишь кусок метода как параметр в виде лямбды, то сначала делаешь руками эту лямбду локальной переменной (ошибиться сложно). А потом жмешь вынести параметр.
Ответь мне ещё раз на вопрос, ты понимаешь, что рефакторинг автоматизируется максимум процентов на 10? Или ты под словом "рефакторинг" понимаешь только автоматизированные в ReSharper паттерны? А то мне начинает казаться, что тема в очередной раз слита: ничего нового ты не написал, зато уже два раза повторил сказанное.
очередной раз слитапосле таких слов .... .... пойду спать
после таких слов .... .... пойду спатьДа нет, я вовсе не собирался тебя обижать, хотя как-то надоело топтаться на одном месте. Но ты ведь и сам можешь посмотреть на паттерны рефакторинга и найти там много того, что не автоматизировано и может вносить ошибки в код. Да и в книгах про рефакторинг всегда упоминаются юнит тесты, и не просто так.
А ты никогда не замечал общее в двух методах, когда это общее можно выделить только с изменением control flow?Главное что это можно сделать маленькими простыми шажками. В общем, тут самое время тебе привести код.
А про то, что при расслоении классов в каком-то методе не будут доступны все поля, используемые ранее?
в рамках нашей дискуссии поля readonly. А readonly поля, пришедшие в аргументе конструктора, и просто параметры метода это почти одно и тоже; в том смысле, что одни в другие (и обратно) преобразовываются маленькими простыми шажками.
Но ты ведь и сам можешь посмотреть на паттерны рефакторинга и найти там много того, что не автоматизировано и может вносить ошибки в код.я там не нашел таких.
Да и в книгах про рефакторинг всегда упоминаются юнит тесты, и не просто так.
это я могу прокомментировать так:
1. Решарпер развивается, в книгах отражена инфа, относящаяся к 3-10 летней давности.
2. Если у человека есть время на написание книги, то у него меньше времени на поддержания формы профессионального программиста. Т.е. книги пишутся людьми, которые находятся слегка на расстоянии от кода.
Главное что это можно сделать маленькими простыми шажками. В общем, тут самое время тебе привести код.Блядь, да при чём тут размер "шажков"? Я тебе в третий раз говорю, что следуя твоим утверждениям, можно сделать вывод о том, что все программы пишутся без ошибок, потому что они точно так же разбиваются на эти твои "маленькие шажки". Возражения есть?
А readonly поля, пришедшие в аргументе конструктора, и просто параметры метода это почти одно и тоже; в том смысле, что одни в другие (и обратно) преобразовываются маленькими простыми шажками.Какой бред. Во-первых, ты не сможешь написать сложную программу с полностью immutable состоянием на императивном языке; то есть в рамках нашей дискусии поля любые, а не только readonly. Во-вторых, readonly поля могут быть mutable. В третьих, дублирование полей в двух классах может вполне себе нарушать DRY (о котором ты сам недавно писал и поэтому может быть неприемлемым и потребует более серьёзных изменений в коде.
я там не нашел таких.Ты вообще на них не посмотрел.
Решарпер развивается, в книгах отражена инфа, относящаяся к 3-10 летней давности.Я не знаю, какой у тебя ReSharper, а в моём около трёх десятков пунктов в меню рефакторинга. В то время как в книгах около 80 паттернов, а на просторах интернета - более сотни. И это только паттерны, а ведь они и близко не перекрывают весь спектр вносимых в код изменений.
Если у человека есть время на написание книги, то у него меньше времени на поддержания формы профессионального программиста. Т.е. книги пишутся людьми, которые находятся слегка на расстоянии от кода.
Если у человека есть время только на кодинг, он так и останется кодером. Тебе следовало бы почитать книжки. Может быть, после этого тебя можно будет считать разработчиком, а не кодером. Да и как можно вести с тобой дискуссию, если ты не признаёшь авторитетов, кроме себя. Извини, но тебе пока что далеко до звания "Великий Непревзойдённый Разработчик", и по этой причине вполне логично было бы аппелировать к третьим лицам.
и по этой причине вполне логично было бы аппелировать к третьим лицам.можно еще к коду. В предыдущем посте я запросил у тебя код? Кода не будет?
можно еще к коду. В предыдущем посте я запросил у тебя код? Кода не будет?Какой именно код тебя интересует и зачем? Открой книгу Фаулера - будет тебе одновременно и ссылка на третье лицо, и код. Я не вижу смысла копировать оттуда текст только для того, чтобы поспорить с упёртым кодером, который считает себя гением.
Какой именно код тебя интересуеткод который демонстрирует вот это
А ты никогда не замечал общее в двух методах, когда это общее можно выделить только с изменением control flow?
зачем?
я покажу последовательность шажков для рефакторинга. Требования к шажкам: автоматические или простое перемещение текста с добавлением скобочек и т.п.
я покажу последовательность шажков для рефакторинга. Требования к шажкам: автоматические или простое перемещение текста с добавлением скобочек и т.пИнтересно, ты троллишь или просто не понимаешь, что это ничему не поможет? Давай лучше вернёмся к твоему изначальному утверждению: ты можешь делать рефакторинг два дня подряд, не запуская проект и никак не тестируя его. При этом ты сказал, что не сделаешь ни одной ошибки.
Я согласен, что можешь, но при двух ограничениях: либо ты будешь держаться в рамках исключительно автоматизированного рефакторинга; либо на что-нибудь простое ты потратишь слишком много времени, потому что тебе придётся всё время интерпретировать код в голове. С этими ограничениями такой рефакторинг становится фактически бесполезным.
Юнит тесты это всё серьёзно упрощают, потому что во-первых, позволяют делать более сложный рефакторинг, который не автоматизирован; во-вторых, позволяют в десятки раз сокращать затрачиваемое на кодирование время.
Ты пытаешься возражать в каком-то не ясном мне ключе, ссылаясь на то, что сложный рефакторинг ты разобьёшь на мелкие части. В четвёртый раз повторю, что это не спасёт тебя от ошибок: даже автоматические тесты всего лишь сводят к близкому к нулю значению их количество.
я покажу последовательность шажков для рефакторинга.Кстати, какой замечательный способ троллинга: надо сначала сказать, что пишешь код три дня подряд "маленькими шажками" абсолютно без ошибок. В ответ на охуевающе-недоумевающие сомнения окружающих в твоей гениальности, предлагаешь задать простую кодерскую задачу, чтобы показать, как закодить её "маленькими шажками", абсолютно без ошибок!
либо на что-нибудь простое ты потратишь слишком много времени, потому что тебе придётся всё время интерпретировать код в голове.не надо интерпретировать код в голове. Надо всего лишь проверять следующее. Каждый шажок это переход кода из состояния n в состояние n+1. Надо гарантировать, что состояние n тождественно состоянию n+1.
Это как в алгебре. Пусть есть уравнение:
a+b+c=0
если мы переносим b вправо, напишем знак минус и уберем ноль
a+c=-b,
то получим тождественное уравнение.
Каждый шажок это переход кода из состояния n в состояние n+1. Надо гарантировать, что состояние n тождественно состоянию n+1.Мы можем это проверить, только интерпретируя код в голове.
Про это мы уже говорили, и я предложил тебе сразу писать программы маленькими шажками и без ошибок.
такими маленькими шажками нельзя написать ничего, потому что вначале программа ничего не делает. На каждом маленьком шажке её поведение не меняется.
Надо всего лишь проверять следующее. Каждый шажок это переход кода из состояния n в состояние n+1. Надо гарантировать, что состояние n тождественно состоянию n+1.Хорошо, уговорил. Вот тебе код, проведи рефакторинг к паттерну Visitor, заменив enum на полиморфные типы и отделив операции Calculate и Derivative от этих типов. При этом ты должен провести этот рефакторинг своими "мелкими шажками", полностью доказав тождественность преобразований каждого "шажка" в обе стороны. При этом ты не имеешь права аппелировать к третьим лицам, и должен доказывать все эти шажки на математических аксиомах.
Чтобы не возникало недопонимания, я сразу скажу, что этот рефакторинг весьма хорошо разжёван в некоторых статьях; причём разжёван он именно до "маленьких шажков", где после каждого такого шажка получается компилируемый рабочий код. Единственное отличие от того, что должен сделать Шурик (а я уверен, что он всё равно спишет из статьи) заключается в том, что в статьях рекомендуют использовать юнит тесты. Я всего лишь хочу показать, насколько этот рефакторинг может быть сложным и длительным, если делать его по шагам; а так же указать на вероятность возникновения потенциальных ошибок в "маленьких шажках". С автотестами этот рефакторинг делается за 10 минут.
enum NodeType
{
CONSTANT,
/// <summary>
/// Переменная X (для простоты задачи других переменных нет).
/// </summary>
VARIABLE_X,
ADD,
SUB,
MULTIPLY
}
class Node
{
readonly Node left;
readonly Node right;
readonly double value;
readonly NodeType type;
/// <summary>
/// Создаёт бинарное выражение.
/// </summary>
public Node(Node left, Node right, NodeType type)
{
this.left = left;
this.right = right;
this.type = type;
}
/// <summary>
/// Создаёт выражение типа константы.
/// </summary>
public Node(double value)
{
this.value = value;
type = NodeType.CONSTANT;
}
/// <summary>
/// Создаёт выражение типа переменной X.
/// </summary>
public Node
{
type = NodeType.VARIABLE_X;
}
/// <summary>
/// Вычисляет выражение.
/// </summary>
/// <param name="variableX">Значение переменной X.</param>
public double Calculate(double variableX)
{
switch (type)
{
case NodeType.ADD:
return left.Calculate(variableX) + right.Calculate(variableX);
case NodeType.SUB:
return left.Calculate(variableX) - right.Calculate(variableX);
case NodeType.MULTIPLY:
return left.Calculate(variableX)*right.Calculate(variableX);
case NodeType.CONSTANT:
return value;
case NodeType.VARIABLE_X:
return variableX;
default:
throw new InvalidOperationException;
}
}
/// <summary>
/// Вычисляет производную выражения.
/// </summary>
public Node Derivative
{
switch (type)
{
case NodeType.ADD:
return new Node(left.Derivative right.Derivative NodeType.ADD);
case NodeType.SUB:
return new Node(left.Derivative right.Derivative NodeType.SUB);
case NodeType.MULTIPLY:
var addLeft = new Node(left.Derivative right, NodeType.MULTIPLY);
var addRight = new Node(right.Derivative left, NodeType.MULTIPLY);
return new Node(addLeft, addRight, NodeType.ADD);
case NodeType.CONSTANT:
return new Node(0);
case NodeType.VARIABLE_X:
return new Node(1);
default:
throw new InvalidOperationException;
}
}
}
На каждом маленьком шажке её поведение не меняется.Нет, на каждом "маленьком шажке" её поведение меняется таким минимальным образом, чтобы это приближало нас к требуемому конечному решению. При этом шажок ну такой маленький, что сделать в нём ошибку просто нереально!1
рефакторинг делается "без изменения поведения", а написание новой с изменением.
проведи рефакторинг к паттерну VisitorВот результат, который меня интересует:
interface INode
{
T ProcessBy<T>(INodeVisitor<T> visitor);
}
interface INodeVisitor<T>
{
T OnConstant(double val);
T OnVariableX;
T OnAdd(INode left, INode right);
T OnSub(INode left, INode right);
T OnMul(INode left, INode right);
}
abstract class BinaryNode : INode
{
protected readonly INode left;
protected readonly INode right;
protected BinaryNode(INode left, INode right)
{
this.left = left;
this.right = right;
}
public abstract T ProcessBy<T>(INodeVisitor<T> visitor);
}
class Add : BinaryNode
{
public Add(INode left, INode right) : base(left, right)
{
}
public override T ProcessBy<T>(INodeVisitor<T> visitor)
{
return visitor.OnAdd(left, right);
}
}
class Sub : BinaryNode
{
public Sub(INode left, INode right) : base(left, right)
{
}
public override T ProcessBy<T>(INodeVisitor<T> visitor)
{
return visitor.OnSub(left, right);
}
}
class Mul : BinaryNode
{
public Mul(INode left, INode right) : base(left, right)
{
}
public override T ProcessBy<T>(INodeVisitor<T> visitor)
{
return visitor.OnMul(left, right);
}
}
class Constant : INode
{
readonly double val;
public Constant(double val)
{
this.val = val;
}
public T ProcessBy<T>(INodeVisitor<T> visitor)
{
return visitor.OnConstant(val);
}
}
class VariableX : INode
{
public T ProcessBy<T>(INodeVisitor<T> visitor)
{
return visitor.OnVariableX;
}
}
class Calculate : INodeVisitor<double>
{
readonly double variableX;
public Calculate(double variableX)
{
this.variableX = variableX;
}
public double OnConstant(double val)
{
return val;
}
public double OnVariableX
{
return variableX;
}
public double OnAdd(INode left, INode right)
{
return left.ProcessBy(this) + right.ProcessBy(this);
}
public double OnSub(INode left, INode right)
{
return left.ProcessBy(this) - right.ProcessBy(this);
}
public double OnMul(INode left, INode right)
{
return left.ProcessBy(this)*right.ProcessBy(this);
}
}
class Derivative : INodeVisitor<INode>
{
public INode OnConstant(double val)
{
return new Constant(0);
}
public INode OnVariableX
{
return new Constant(1);
}
public INode OnAdd(INode left, INode right)
{
return new Add(left.ProcessBy(this right.ProcessBy(this;
}
public INode OnSub(INode left, INode right)
{
return new Sub(left.ProcessBy(this right.ProcessBy(this;
}
public INode OnMul(INode left, INode right)
{
var addLeft = new Mul(left.ProcessBy(this right);
var addRight = new Mul(right.ProcessBy(this left);
return new Add(addLeft, addRight);
}
}
рефакторинг делается "без изменения поведения", а написание новой с изменением.Смотря что понимать под изменением. Замену алгоритма O(N) на O(LogN) или улучшение функциональности пользовательского интерфейса можно тоже считать рефакторингом. Но даже если и не считать, с чем я не намерен сейчас спорить, то моё утверждение всё равно остаётся в силе, если принимать на веру, что в "маленьких шажках" невозможно ошибиться.
А если не принимать это на веру, то становится очевидно, что вероятность появления ошибки возрастает вместе с количеством этих "маленьких шажков", даже если каждый отдельно взятый из них ничего не портит. Нам всего-то нужно либо ошибиться в одном из них, либо перепутать их порядок.
Поля конечно readonly, но они инициализируются не во всех конструкторах. Так пишут только плохие ООП-шники. Интересно рассматривать рефакторинг кода без таких детсадовских косяков.
Поля конечно readonly, но они инициализируются не во всех конструкторах. Так пишут только плохие ООП-шники.
Если бы везде были только хорошие ООП-шники, то понятие рефакторинга никогда бы не появилось.
Интересно рассматривать рефакторинг кода без таких детсадовских косяков.Либо пиши код, либо мы засчитываем тебе очередной слив.
Только рефакторингом программу не напишешь. Ибо она будет торждественна ничегонеделанью.
Тесты в общем-то как раз и проверяют эту торждественность. Я прицепился к твоему аргументу, что если "можно сделать маленький торждественный шажок без ошибки", то
"можно написать любую программу без ошибок".
Это неверное утверждение.
Я прицепился к твоему аргументу, что если "можно сделать маленький торждественный шажок без ошибки", то "можно написать любую программу без ошибок".Бля, не надо думать, что я тупой; и не надо придираться к словам. Я думаю, что кто хотел - тот понял меня правильно.
Тесты в общем-то как раз и проверяют эту торждественность.Странно, почему ты тогда не написал, что первый "маленький шажок" в программе просто исправляет ошибку нетождественного преобразования: из функционирующего кода мы сделали пустой. Теперь первым шагом разработки новой программы будет откат этого маленького ошибочного шага или что-то в этом роде...
В общем, без проблем я это проделал. Описывать получится довольно долго, опишу в ближайшие дни как найду время.
Хорошо, уговорил. Вот тебе код, проведи рефакторинг к паттерну Visitor, заменив enum на полиморфные типы и отделив операции Calculate и Derivative от этих типов. При этом ты должен провести этот рефакторинг своими "мелкими шажками", полностью доказав тождественность преобразований каждого "шажка" в обе стороны. При этом ты не имеешь права аппелировать к третьим лицам, и должен доказывать все эти шажки на математических аксиомах.Вот последовательность шажков. Пощу в pdf поскольку неохота переносить форматирование:
interface INode
{
T ProcessBy<T>(INodeVisitor<T> visitor);
}
interface INodeVisitor<T>
{
T CONSTANTNode(double value);
T VARIABLE_XNode;
T ADDNode(INode left, INode right);
T SUBNode(INode left, INode right);
T MULTIPLYNode(INode left, INode right);
}
class Derivative : INodeVisitor<INode>
{
public INode CONSTANTNode(double value)
{
return new CONSTANTNode(0);
}
public INode VARIABLE_XNode
{
return new CONSTANTNode(1);
}
public INode ADDNode(INode left, INode right)
{
return new ADDNode(left.ProcessBy(this right.ProcessBy(this;
}
public INode MULTIPLYNode(INode left, INode right)
{
var addLeft = new MULTIPLYNode(left.ProcessBy(this right);
var addRight = new MULTIPLYNode(right.ProcessBy(this left);
return new ADDNode(addLeft, addRight);
}
public INode SUBNode(INode left, INode right)
{
return new SUBNode(left.ProcessBy(this right.ProcessBy(this;
}
}
class Calculate : INodeVisitor<double>
{
private readonly double variableX;
public Calculate(double variableX)
{
this.variableX = variableX;
}
public double ADDNode(INode left, INode right)
{
return left.ProcessBy(this) + right.ProcessBy(this);
}
public double CONSTANTNode(double value)
{
return value;
}
public double MULTIPLYNode(INode left, INode right)
{
return left.ProcessBy(this) * right.ProcessBy(this);
}
public double SUBNode(INode left, INode right)
{
return left.ProcessBy(this) - right.ProcessBy(this);
}
public double VARIABLE_XNode
{
return variableX;
}
}
class CONSTANTNode : INode
{
readonly double value;
public CONSTANTNode(double value)
{
this.value = value;
}
public T ProcessBy<T>(INodeVisitor<T> visitor)
{
return visitor.CONSTANTNode(value);
}
}
class VARIABLE_XNode : INode
{
public T ProcessBy<T>(INodeVisitor<T> visitor)
{
return visitor.VARIABLE_XNode;
}
}
class ADDNode : BinaryNode
{
public ADDNode(INode left, INode right) : base(left, right)
{
}
public override T ProcessBy<T>(INodeVisitor<T> visitor)
{
return visitor.ADDNode(left, right);
}
}
class SUBNode : BinaryNode
{
public SUBNode(INode left, INode right) : base(left, right)
{
}
public override T ProcessBy<T>(INodeVisitor<T> visitor)
{
return visitor.SUBNode(left, right);
}
}
class MULTIPLYNode : BinaryNode
{
public MULTIPLYNode(INode left, INode right) : base(left, right)
{
}
public override T ProcessBy<T>(INodeVisitor<T> visitor)
{
return visitor.MULTIPLYNode(left, right);
}
}
abstract class BinaryNode: INode
{
protected readonly INode left;
protected readonly INode right;
protected BinaryNode(INode left, INode right)
{
this.left = left;
this.right = right;
}
public abstract T ProcessBy<T>(INodeVisitor<T> visitor);
}
конечно это всё гораздо легче воспринимается, если показать в действии.
Бля, не надо думать, что я тупой;в первую очередь возникает подозрение не в тупизне, а в том, что ты не слышишь, что тебе говорят (да еще обвиняешь в это же собеседника). Касательно той твоей фразы, я воспринимал ее ровно также как .
Если бы везде были только хорошие ООП-шники, то понятие рефакторинга никогда бы не появилось.Это далеко не так. Рефакторинг это вполне обычный инструмент в команде хороших программистов. Например, в примере с визитором, если бы не делали неинициализируемых филдов, то это был бы вполне себе хороший код даже без визитора. В моей последовательности шажков самый большой из трех это первый шаг, который как раз фиксит эту детсадовский косяк.
Либо пиши код, либо мы засчитываем тебе очередной слив.вот теперь интересно сможешь ты здраво взглянуть на или очередной раз затролишь, не поняв и не разобравшись в вопросе ... завтра посмотрим . Ставлю девять против одного, что затролишь.
Вот последовательность шажков. Пощу в pdf поскольку неохота переносить форматирование:Пока что ответ не принимается: нет ни обоснования каждого шага, ни кода после каждого шага, в документе куча детских ошибок.
Ты сам-то прочитал свой PDF? Например:
1. "Строчка return new VARIABLE_XNode(1); небилдиться." - нигде выше в документе не написано, как эта строчка появилась. Вообще весь документ страдает от этого.
2. "Заменяем на return new CONSTANTNode(1); Она опять не билдится, но на это не обращаем внимания" - твои "маленькие шажки", особенно когда ты кинулся на спасательный круг их "тождественности", не могут приводить к некомпилируемому результату.
3. "Красным подсветится не верный тип у возвращаемого значения BinaryNode." - я не понял, откуда взялось возвращаемое значение у класса? Может быть, не стоило в спешке выплёвывать документ, в котором столько детсадовских ошибок и неточностей?
4. "Там где конструктор вызывался с третьим параметром подсветиться красным; переносим значение третьего параметра в название конструктора." - это не является "тождественным маленьким преобразованием".
5. "С помощью решарпера сгенерировали методы в интерфейсе INodeVisitor." - опять исправление некомпилируемоего кода, которое очевидно не является "тождественным маленьким преобразованием".
6. "Вырезаем метод qwe и переносим в класс Derivative." - и код опять не компилируется.
7. "по Alt+Enter добавляем в метод параметры left и right." - опять-таки, перестаёт компилироваться класс ADDNode, при чём нигде не сказано, что ты с этим сделал сейчас (введя значения по умолчанию) или потом (добавив их руками).
вот теперь интересно сможешь ты здраво взглянуть на факты или очередной раз затролишь, не поняв и не разобравшись в вопросе ... завтра посмотрим . Ставлю девять против одного, что затролишь.Да ты сам себя уже затроллил. Во-первых, неграмотно составив документ. Во-вторых, использовав не просто шаги без доказательств тождественности, но и шаги, приводящие к некомпилируемому результату. В-третьих, не доведя дело до конца.
Но самое главное, если ты исправишь эти недочёты, то просто ещё раз докажешь мою правоту: надо либо тратить слишком много времени на рефакторинг, либо при этом будут появляться ошибки.
затролишьА вот как я бы затроллил:
Это как в алгебре. Пусть есть уравнение:Пусть есть уравнение "a+b+c=0", которое надо привести к "a+c=-b". Сначала поделим "c" на ноль "a+b+c/0=0", при этом выражение теряет смысл, но это не важно, потому что мы вынесем "b" направо "a+c/0=0-b". Теперь прибавим корень из -1 к правой части выражения: "a+c\0=0-b+√-1", после чего умножаем часть "c/0" на ноль, что даёт обратно "c": "a+c=0-b+√-1". Выражение до сих пор не очень осмысленно, но мы сейчас прибавим корень из -1 и к левой его части, при этом ноль справа уйдёт: "a+c+√-1=-b+√-1". Теперь надо просто вычесть корень из -1 из обеих его частей, вуаля: "a+c=-b".
a+b+c=0
если мы переносим b вправо, напишем знак минус и уберем ноль
a+c=-b,
то получим тождественное уравнение.
Пока что ответ не принимается: нет ни обоснования каждого шага, ни кода после каждого шага, в документе куча детских ошибок.Итак, решение задачи есть в документе. Осталось только проблема твоего понимания этого документа. Как обычно это бывает, время затрачиваемое на понимание того или иного вопроса сильно зависит от того настроен ли человек на то, чтобы понять или настроен против; разница времен бывает на порядки. Если настрой “против”, то это займет месяца, я такого себе позволить не могу. Пока у тебя проявляется настрой “против”, но я напишу еще несколько постов в надежде, что ты поменяешь свой настрой.
Проиграй по шагам то, что написано в документе, там, где не очень ясно написано, пости сюда, я опишу аккуратнее/подробнее.
Как и в прошлый раз, я предполагаю, что разговариваю с достаточно опытным C# программистом и пользователем ReSharper-а. Если, как в прошлый раз, есть какие-то пробелы в твоих знаниях (в прошлый раз ты не знал как билдиться ASP.NET сайт то прошу постараться как можно раньше сообщить об этом (в этом состоит одно из проявлений положительного настроя на понимание).
Пусть есть уравнение "a+b+c=0", которое надо привести к "a+c=-b". Сначала поделим "c" на ноль "a+b+c/0=0", при этом выражение теряет смысл, но это не важно, потому что мы вынесем "b" направо "a+c/0=0-b". Теперь прибавим корень из -1 к правой части выражения: "a+c\0=0-b+√-1", после чего умножаем часть "c/0" на ноль, что даёт обратно "c": "a+c=0-b+√-1". Выражение до сих пор не очень осмысленно, но мы сейчас прибавим корень из -1 и к левой его части, при этом ноль справа уйдёт: "a+c+√-1=-b+√-1". Теперь надо просто вычесть корень из -1 из обеих его частей, вуаля: "a+c=-b".Я так понимаю это аналогия твоего текущего понимания моего документа? Если, да, то ты в корне ошибаешься, в документе, как раз, только нужная инфа в краткой форме.
Итак, решение задачи есть в документе.Решения задачи нет в документе, потому что в нём нету последовательности "маленьких тождественных шагов", перепутаны или отсутствуют важные события, нету кода для демонстрации происходящего. Я предлагаю тебе либо закончить этот балаган, либо написать документ по-нормальному. Во втором случае ты просто подтвердишь мою правоту: время или ошибки.
Проиграй по шагам то, что написано в документе, там, где не очень ясно написано, пости сюда, я опишу аккуратнее/подробнее.Сначала напиши шаги так, чтобы после каждого шага код компилировался и работал правильно. Иначе теряется твой единственный на данный момент аргумент: "маленькие тождественные шажки".
Если, как в прошлый раз, есть какие-то пробелы в твоих знанияхТы уже в который раз наезжаешь на мои знания, причём настолько забавно, как будто ребёнок пытается что-то себе доказать. Я бы рекомендовал тебе сначала заняться своими, например ты мог бы научиться писать документы; или почитать книжки и от кодерства перейти к разработке.
Сначала напиши шаги так, чтобы после каждого шага код компилировался и работал правильно. Иначе теряется твой единственный на данный момент аргумент: "маленькие тождественные шажки".Такого утверждения я не делал. Разбитие на шажки, как ты видишь, иерархическое. На этапе мелких шажков код может не компилироваться. Как пример: в нашем примере допускается, что код компилируется и работает только на рутовых шагах 1, 2, 3. Разбитие на мелкие шажки нужно лишь для того, чтобы человек не сделал ошибки. Не сделать ошибки человеку помогает компилятор/решарпер. Когда нам встречается достаточно крупный шажок, который мы не можем сделать без ошибок, мы ищем/придумываем/знаем с опытом, какой маленький шажок нам надо сделать, чтобы решарпер с помощью подсветок ошибок/предупреждений проконтролировал нас на протяжении всего крупного шажка. Кроме компилятора еще помогают туду. И, возможно, еще какие-то фичи среды разработки. Короче, если проиграешь пример, то будут более конкретные вопросы. Это тот случай, когда надо идти от частности, чтобы понять общую суть (о способы понимания обсуждались, кажется, в разделе Society).
Разбитие на мелкие шажки нужно лишь для того, чтобы человек не сделал ошибки.Забыл: еще в результате проигрывания шажков проверяется, что наше понимание кода совпадает с самим кодом.
Забыл: еще в результате проигрывания шажков проверяется, что наше понимание кода совпадает с самим кодом.Т.е., посмотрев на код, мы делаем некоторую гипотезу, а проигрывание шажков нам гарантирует, что из этой гипотезы нет досадных исключений.
что из этой гипотезы нет досадных исключений.если есть, то нас спасает Undo или TFS
Такого утверждения я не делал.Какого утверждения ты не делал? Сначала ты просто писал про "маленькие шажки" и всё. Потом был наезд от -а на их тождественность, и ты под этим наездом подписался. Если ты делаешь просто какие-то "маленькие шажки", то я в пятый раз повторяю: почему бы тебе сразу не сказать, что ты пишешь код вообще без ошибок.
Разбитие на мелкие шажки нужно лишь для того, чтобы человек не сделал ошибки. Не сделать ошибки человеку помогает компилятор/решарпер.
Ты утверждал, что можешь делать эти "мелкие шажки" без ошибок два дня подряд, вообще без тестирования. Пока что это необоснованный выпендрёж.
Как пример: в нашем примере допускается, что код компилируется и работает только на рутовых шагах 1, 2, 3.А вот в книжках этого не допускается: там почему-то код компилируется, работает и проходит тесты на каждом маленьком шаге.
На этапе мелких шажков код может не компилироваться.Тогда ты не можешь обосновать свою позицию. Из чего следует, что ты либо вообще не можешь два дня делать рефакторинг без какого-то вида тестирования; либо делаешь при этом кучу ошибок, которые потом приходится разгребать.
учим тип Node быть полиморфным
добавляем интерфейс INode с функциями Calculate/Derivate
заменяем тип Node на INode в полях, конструкторах и функциях
методы Calculate и Derivative делаем виртуальными
поля в Node делаем protected
выносим Constant в отдельный класс
добавляем класс Constant отнаследованный от Node. конструктор делаем public Constant(double val) : base(val) { }
перекрываем Constant.Calculate копируя функционал из Node.Calculate
перекрываем Constant.Derivative копируя функционал из Node.Derivative
по коду заменяем создания константы через Node на создание через Constant
удаляем поддержку Constant из Node: добавляем protected constructor Node(NodeType переносим поле value в Constant,
удаляем конструктор из Node для Constant, удаляем ветку constant из Calculate/Derivative
аналогично добавлению Constant добавляем типы Add, Sub, Mul, VariableX
методы Node.Calculate, Node.Derivative и класс Node делаем абстрактными
классы Constant, VariableX напрямую наследуем от INode
убираем enum NodeType
класс Node переименовываем в BinaryNode
переносим функциональность из Node-ов в Visitor-ы
добавлем интерфейс INodeVisitor
добавляем метод INode.ProcessBy и реализуем его в наследниках
добавляем класс Calculate и реализуем его, копируя в него последовательно методы Calculate из наследников Node меняя сигнатуру,
и заменяя в теле вызовы Calculate на ProcessBy
добавляем класс Derivative, аналогично классу Calculate
прибиваем методы INode.Calculate/Derivative и их реализацию
так же все эти пункты можно выполнять без юнит-тестов, но один большой полный регрессионный тест, конечно, стоит прогонять для избежания опечаток
вот последовательность минимальных шажков, после каждого пункта код успешно компилируется и решает задачу не хуже исходного кодаВо-первых, без примеров кода читать эти пункты слишком тяжело.
соответственно, по всем этим пунктам можно легко показать, что каждый из них не ухудшает код с точки зрения правильности выполнения
Конечно, можно. Не знаю, можно ли это показать по твоим шагам, потому что в них тяжело вникнуть. Но у Фаулера это всё детально расписывается в виде двух или трёх независимых паттернов. И при этом он всё равно рекомендует использовать тестирование.
так же все эти пункты можно выполнять без юнит-тестов, но один большой полный регрессионный тест, конечно, стоит прогонять для избежания опечатокТы вместо Шурика подтвердил истинность моего высказывания. Чтобы во время даже очень простого рефакторинга приблизить вероятность ошибки к той, которая получается при наличии тестирования, мы должны потратить очень много времени. При наличии автоматического тестирования многие маленькие пункты можно пропускать, продолжительное время содержать код в некомпилируемом состоянии, и так далее; фактически, при меньших временных затратах, получая достаточно низкую вероятность ошибиться.
В частности, я отрефакторил свой же пример за несколько минут: сначала написал юнит тесты, в этом примере очень легко сделать почти 100% покрытие; затем выполнил рефакторинг за два глобальных шага, при этом код не компилировался от начала и до конца.
Кстати, вы писали свои "маленькие шаги" уже после того, как я дал вам готовый результат (кстати, он вообще рабочий или нет? )
это самообман. тесты на реальных задачах обеспечивают покрытие в гомеопатических дозах, соответственно без обоснования почему каждый переход легитимен - очень легко упустить кучу нюансов: те же самые хаки легко могут быть пропущены и не перенесены в обновленную версию.
тесты на реальных задачах обеспечивают покрытие в гомеопатических дозахНа реальных задачах автоматические тесты обеспечивают как минимум покрытие функциональности. Я ещё раз сошлюсь на Фаулера (кстати, на кого ссылаешься ты?). Он не отрицает, что при использовании юнит тестов есть deminishing returns и что нельзя сделать 100% покрытие, перечисляя каждый int 2^32 раза. Но тем не менее в книге утверждается, что если не обращать внимания на теоретиков, а заниматься практикой, то вполне можно достичь оптимального, качественного и разумного результата.
В этом случае, как постоянно практикующий разработчик, я могу лишь полностью согласиться с Фаулером. Автоматические тесты позволяют не только быстрее делать рефакторинг, но и быстрее реализовывать и расширять функциональность кода программы, избегать регрессии, улучшать понимание кода разработчиками.
Кстати, а каковы причины ставить в корень такой иерархии интерфейс, а не класс?
Кстати, а каковы причины ставить в корень такой иерархии интерфейс, а не класс?А какой класс ты предлагаешь поставить в корень этой иерархии?
Абстрактный Node. Этот вариант более ограничивающий, но это и хорошо, потому что я не могу придумать ситуации, когда в иерархию нодов потребовалось бы вписывать класс, состоящий в другой иерархии.
Абстрактный Node. Этот вариант более ограничивающий, но это и хорошо, потому что я не могу придумать ситуации, когда в иерархию нодов потребовалось бы вписывать класс, состоящий в другой иерархии.Зачем заменять интерфейс на абстрактный класс?
кстати, он вообще рабочий или нет?Мой код рабочий, если изначальный код с enum-ами был рабочий. Кстати, это, наверное, даже наиболее важное преимущество такого стиля работы с кодом: тебе не обязательно вникать, что код делает. Я не смотрел, как там что у тебя вычислялось и derivative-тилось. Обычно схема такая. Перед программистом ставится конкретная задача. Он смотрит некоторое время в код, если всё хорошо, то просто решает задачу. Если на текущий код задача ложиться криво, то требуется рефакторинг. Тот стиль рефакторинга, который я показал, позволяет свести к минимуму требуемый программисту объем сведений о том, что делает код. Код просто преобразовывается по формальным правилам. А вот если тесты нельзя поправить без специальных знаний о функциональности кода, то это порождает целый ряд вопросов: где эту информацию брать, если из документов, то кто-то должен поддерживать документы в актуальном состоянии; при чтении документов возникают вопросы, кто-то должен на них отвечать и т.д. и т.п. Да, понять, что требуется рефакторинг можно на неполных знаниях.
P.S. Мы же про апликухи типа опердени, а там, чем меньшим объемом инфы надо обмениваться между людьми, тем лучше.
Мой код рабочий, если изначальный код с enum-ами был рабочий.Извини, но это уже неважно. Рабочий пример только у , а твой документ не состоит из тождественных шагов. Пока ты это не исправишь, я не вижу смысла обсуждать поставленную мною задачу: её решил , но не решил ты.
А вот если тесты нельзя поправить без специальных знаний о функциональности кода
Из этой фразы следует, что ты не разбираешься в том, что такое атоматическое тестирование. Из чего следует, что ты не сделал ни одного проекта, хотя бы на 10% покрытого автотестами. Из этого, а так же из того, что ты не признаёшь авторитетов следует, что вести с тобой конструктивный спор вообще бессмысленно. Как и в споре про Java, ты пытаешься рассуждать о вещах, которые тебе не известны и не опробованы тобою на практике.
если тесты нельзя поправить без специальных знаний о функциональности кодапо-моему ценность автотестов заключается также и в том, что они являются наиболее рабочей и точной документацией кода (в том числе "функциональности кода", хотя это какое-то мутное словосочетание)
по-моему ценность автотестов заключается также и в том, что они являются наиболее рабочей и точной документацией кодаТак вот не надо эту “документацию” пропускать через свою голову в бОльшем объеме, чем это требует текущая задача. (Не путать с обзорным пониманием системы, оно конечно необходимо; речь идет о мелких деталях.)
Из чего следует, что ты не сделал ни одного проекта, хотя бы на 10% покрытого автотестами.В 2005-2006 годах я был таким же фанатам unit-тестов как ты сейчас. Со временем пришло более здравое понимание.
опердень у людей в головах!
В 2005-2006 годах я был таким же фанатам unit-тестов как ты сейчас.Тогда странно, что ты при этом не прочитал ни одной книжки. Странно, что ты не смог в этой теме предложить ни одного аргументированного минуса автоматического тестирования, хотя их достаточно много. Странно, что ты всё время аргументировал тем, какой ты крутой, включая и этот последний пост.
Со временем пришло более здравое понимание.Видимо, к тебе пришло понимание, что ты делаешь что-то не так, но ты сам, упиваясь своей крутизной, не смог в этом разобраться. Поэтому и прекратил развиваться в этом направлении.
Из этого, а так же из того, что ты не признаёшь авторитетов ...От чего же ты так решил?
Кстати, ты в соседнем треде упоминал Фаулера в качестве авторитета. Но вот в его известной книге про Архитектуру корпоративных приложений, есть такой наивняк в коде (могу указать, если надо что практикующим программистом его уж никак не назовешь. Но книжки, тем не менее, нужны, они выполняют свою роль по обмену информацией в мировом масштабе. Фаулер кое-что систематизировал, да, за это ему спасибо, обсуждать эти темы стало проще. Но у тебя какое-то неоправданно трепетное отношение к книгам по производству софта аля опердень. Это тебе не академические книги, не стоит к ним так трепетно относиться.
Но вот в его известной книге про Архитектуру корпоративных приложений, есть такой наивняк в коде (могу указать, если надо что практикующим программистом его уж никак не назовешь.У него много чего есть, и минорные ошибки в статьях, и сомнительные паттерны типа "Split Loop". Но кроме Фаулера тесты описаны и у других авторов, которых я упоминал. Фаулер был приведён в качестве аргументов про полезность тестирования для рефакторинга. К сожалению, его, а не твоя, книга считается каноническим примером по рефакторингу, а поэтому более чем глупо с твоей стороны наезжать на его работу. Ты ещё Пушкина стихи поучил бы писать. (Это гипербола, если вдруг чувство юмора кого-то опять подведёт.)
Тогда странно, что ты при этом не прочитал ни одной книжки.мне вроде хватило Интернета (библиографический список не виду ).
Странно, что ты не смог в этой теме предложить ни одного аргументированного минуса автоматического тестирования, хотя их достаточно много.
а у меня хотя бы намеренье такое было? (Кстати, посмотри первый пост первоначального треда.)
Странно, что ты всё время аргументировал тем, какой ты крутой, включая и этот последний пост.
Про мою крутизну и мою гениальность пишешь только ты. Нас тут это только забавляет .
Видимо, к тебе пришло понимание, что ты делаешь что-то не так, но ты сам, упиваясь своей крутизной, не смог в этом разобраться. Поэтому и прекратил развиваться в этом направлении.
Вряд ли, поскольку в других многих направления я замечаю свое развитие со временем, вряд ли это является каким-либо исключением.
а у меня хотя бы намеренье такое было? (Кстати, посмотри первый пост первоначального треда.)В первом посте ты сказал, что по твоему опыту невозможно писать тесты для каких-то приложений, и что их стоимость существенно увеличивается. Кроме ссылок на самого себя ты никак больше не аргументировал это заявление.
Про мою крутизну и мою гениальность пишешь только ты.
Так вот, в статически типизированных языках широкий класс изменений можно вносить
так, что вероятность появления багов крайне мала.
да, чё там это ж всё очевидно практикующему девелоперу
Я могу рефакторить два дня и не внести ни одной баги. Без автотестов.
точнее даже вообще ни разу не побывав в рантайме.
В 2005-2006 годах я был таким же фанатам unit-тестов как ты сейчас. Со временем пришло более здравое понимание.
Вряд ли, поскольку в других многих направления я замечаю свое развитие со временем, вряд ли это является каким-либо исключением.
Видимо, является, и не только это.
В первом посте ты сказал, что по твоему опыту невозможно писать тесты для каких-то приложений, и что их стоимость существенно увеличивается. Кроме ссылок на самого себя ты никак больше не аргументировал это заявление.да, где мое намеренье на развертывание этой аргументации?
Так вот, в статически типизированных языках широкий класс изменений можно вносить
так, что вероятность появления багов крайне мала.
да, чё там это ж всё очевидно практикующему девелоперу
Я могу рефакторить два дня и не внести ни одной баги. Без автотестов.
точнее даже вообще ни разу не побывав в рантайме.
В 2005-2006 годах я был таким же фанатам unit-тестов как ты сейчас. Со временем пришло более здравое понимание.
кх, это заявления гения?
Видимо, является, и не только это.
Мне бросить заниматься программированием?
Мне бросить заниматься программированием?хотя ведь это ты же не понял мою последовательность шажков. А она же работает. Я ее два раза проделывал, первый раз и когда описывал.
кх, это заявления гения?Конечно, нет. Это заявления самоуверенного кодера, который считает себя гением.
хотя ведь это ты же не понял мою последовательность шажков. А она же работает. Я ее два раза проделывал, первый раз и когда описывал.
Твою последовательность шажков я понял, и написал про неё развёрнутый ответ: поставленную задачу ты не решил.
Твою последовательность шажков я понял, и написал про неё развёрнутый ответ: поставленную задачу ты не решил.Я прокомментировал твой ответ: ты не понял мое решение. Я бы даже сказал, что не пытался понять. Оно работает.
Я прокомментировал твой ответ: ты не понял мое решение. Я бы даже сказал, что не пытался понять. Оно работает.Речь не идёт о том, работает что-то или нет. Речь идёт о том, что твоё решение не является доказательством того, что ты вообще умеешь делать тождественные шаги при рефакторинге. Шаги, приводящие к некомпилируемому результату не являются тождественными. Глобальные шаги не являются маленькими.
Речь идёт о том, что твоё решение не является доказательством того, что ты вообще умеешь делать тождественные шаги при рефакторинге. Шаги, приводящие к некомпилируемому результату не являются тождественными. Глобальные шаги не являются маленькими.Мой ответ на это уже был:
Я уже много кратно про себя отмечаю, что ты повторяешь свои вопросы опять и опять (некоторые по-моему более 5 раз).
Я уже много кратно про себя отмечаю, что ты повторяешь свои вопросы опять и опять (некоторые по-моему более 5 раз).Я повторяю их только тогда, когда кто-то не может меня понять с первого раза. Знаешь, как детей учат газговаривать. Давай ещё раз: твои шаги не являются тождественными. Глобальные шаги не являются маленькими. Из этого следует, что ты вообще не умеешь делать рефакторинг без тестирования и без рантайма.
Из этого следует, что ты вообще не умеешь делать рефакторинг без тестирования и без рантайма.у взрослого дяди плохо с элементарной логикой
у взрослого дяди плохо с элементарной логикойСлив засчитан.
у взрослого дяди плохо с элементарной логикойвоо , наверное поэтому ты без тестов не можешь рефакторить
Слив засчитан.твой, да
твой, даМожешь троллить дальше сколько угодно. Итог этой темы никак от этого не изменится: ты выпендривался, а в итоге не смог провести простой рефакторинг по шагам.
Можешь троллить дальше сколько угодно. Итог этой темы никак от этого не изменится: ты выпендривался, а в итоге не смог провести простой рефакторинг по шагам.Итог в том, что у тебя маленький скил по работе с кодом.
Итог в том, что у тебя маленький скил по работе с кодом.Возможно. Но дело-то в том, что итог ещё и такой: у тебя этот скилл раз в пять меньше моего.
Возможно. Но дело-то в том, что итог ещё и такой: у тебя этот скилл раз в пять меньше моего.у меня больше настолько, что различие уже качественное.
у меня больше настолько, что различие уже качественное.Да что ты говоришь, а в дверях он у тебя не застревает? За облака не задевает?
Интуиция, не подкреплённая реальным опытом, говорит мне что используя тут интерфейс вместо класса, мы получаем совершенно ненужную (буду рад, если опровергнешь) гибкость, платя за неё (совсем немного, ну и что?) повышенную цену диспатчинга вызовов методов интерфейса.
Зачем заменять интерфейс на абстрактный класс?
Интуиция, не подкреплённая реальным опытом, говорит мне что используя тут интерфейс вместо класса, мы получаем совершенно ненужную (буду рад, если опровергнешь) гибкость, платя за неё (совсем немного, ну и что?) повышенную цену диспатчинга вызовов методов интерфейса.Твоя интуиция тебя подводит. Ты можешь убедиться в этом, когда сам заменишь интерфейс на абстрактный класс и посмотришь на код.
abstract class Node {
abstract public T ProcessBy<T>(INodeVisitor<T> visitor);
}
вместо
interface INode {
T ProcessBy<T>(INodeVisitor<T> visitor);
}
Также добавились модификаторы override, а абстрактный метод ProcessBy в BinaryNode стал не нужен.
Что ты имел в виду, я не понял.
Что ты имел в виду, я не понял.Прочитай своё предположение, после чего посмотри на ключевое слово "abstract" перед методом ProcessBy.
Оставить комментарий
6yrop
я правильно понимаю, что ты намекаешь, что без автотестов рефакторинг нельзя делать? ну скажем почти всегда, так?