Понимаете, я вообще обучался на физ-мате. И есть такое наблюдение. В физике требуется некоторая сила ума. Там не нужно «многознание». Человек может не помнить и десятка формул, но знать всё. Всё вывести.
В программировании я часто встречаю обратное — когда этот интеллект заменяют попугайство-хвастовством. Реально, человек не может код с десяток строк написать, где цвет от строки к строки менялся линейно, но считает дураками всех, кто не знает, что контрл+б в редакторе — что-то делает в обход менюшке. К счастью, таких программистов, которые реально думают, что знать несколько десятков слов и быть в треде — это самое важное — не так много.
С другой стороны, программирование не настолько и простое в сравнении с другими науками. Есть база, computer science — которые не меняются десятилетиями.
Конечно, есть большой пласт программистов, которые целью своей жизни считают прыгание по фреймворкам, мышкой что-то там делание и получение каких-то сайтов или что-то модное даже и не кодить.
Я нигде не утверждаю, что программисту не стоит ничему учиться и держаться за старое. И бояться нового. Но по-моему, и думаю, опытные программисты меня поддержат — новое важно применять, но с оглядкой. Ни в коем случае не согласен, что самое главное — это фичи, даже не смотря на ошибки. Устоявшиеся вещи хоть немного себя зарекомендовавшие надо применять. Риски меньше. Думаете, так только админы думают из страха? Конечно, тормоза придумали трусы.
Странный немного вывод. Я к админству совсем никак не отношусь. Мало того, кодить, на мой взгляд, хорошо получается.
Может быть у вас собрался такой коллектив, где только такие программисты. Конечно, программист не должен жить только на своем коде и писать велосипеды. Всё, что упрощает деятельность — в копилку. И учиться надо новому постоянно. Другое дело, надо не забывать смотреть на цель — зачем этот проект. Не использовать его для фана за деньги заказчика. Молодые не очень опытные программисты часто этим болеют. Пытаются попробовать всё в проекте, а не дома. Нужно это или нет. Причем, по поводу каждой мелочи. Иногда код написать для мелкой задачи — полстраницы, программист будет изучать неделями новые фреймворки, чтобы всунуть чей-то комбайн.
Вообще, в программировании это болезнь. Я бы сказал, что у программистов очень низкий уровень образования (вот так с плеча), сам преподавал им когда-то. Из-за этого, особенно молодые программисты, считают, что крутость заключается в том, сколько мелочей они услышали и запомнили. Какую-то комбинацию кнопок — и считают, что достигли интеллектуальной вершины. Есть такой тип программистов — поверхностно-обзоро-читатели. Каждый день или через день, они ставят новые фишки домой, почти как девочки в социальных сетях статусы меняют.
Не могут ничего или мало, но говорят обо всём.
Еще, может не правильно донес идею. Заказчик в посте — это не обязательно внешний заказчик. Это некий символ исходящих требований. Заказчиком может быть и сам программист. Программа обычно служит для какой-то цели, в какой-то предметной области. И код, как описание, должен ее описывать.
Плохой код — это код, в котором не видно такого описания. Конечно, почти нельзя так добиться, чтобы везде в коде было только описание того, что нужно. Но отрицательная шкала оценок кода снимает это противоречие. Код и так плохой. Хуже тот, который не имеет отношения к описанию. Еще более худший код — код, который вообще ничего не описывает. Желательно, чтобы код был читаем локально. Без знаний «архитектуры». Открыв любую страницу кода, хоть там и тысячи страниц, на ней должны читаться намерения.
К сожалению, люди с положительной шкалой скатываются в противоположность. Когда думают, что процент кода небольшой, который прямо решает задачу, а остальное — кирпичики — начинают писать программу ради программы. Это вроде как бы сняли фильм, где во всех подробностях описывается съемка фильма и ничего более. Как фильм, такой может существовать. Процент людей, которым он будет интересен — крайне мал.
А в программировании всё ровно наоборот. Таких бессюжетных фильмов те самые 95 процентов. Программисты увлекаются чем угодно, что касается самого процесса — оптимизациями, многопоточностью, паттернами с гибкостью, распределенной архитектурой. Послушайте о чем они говорят и где лежит сфера их интересов в основном.
Программист, полагая, что без всего этого не обойтись, и имея положительную шкалу оценок — ставит это во главу угла. Получается бессюжетный код.
Я вообще слабо понимаю, что имел ввиду Гилберт. Слово «фантазия» для меня слабо определено. Попытался свое понимание Гилберта описать с объяснением, что в данном случае есть фантазия.
Математика действительно требует хорошей гибкости ума и внимание, чтобы удержать в уме сложную модель. Но при этом математика — это набор правил, формальных, синтаксических, от которых не отклоняются. Решения задач — это формулы, правила с которыми строго определены. Одна механика.
В посте я больше подразумевал науки, которые изучают природу. Математика в принципе — это аппарат для них.
Всё верно — до начала реализации идет работа с заказчиком. Требования обсуждаются, принимаются. Идиотские отклоняются. Идет плотное и не одностороннее общение. После принятия требований — никакого креатива.
Но считать заказчика идиотом, даже если это так, плохо. Много раз замечал тенденцию, когда программисты думают, что они лучше знают, что надо заказчику, чем он сам.
Жесткий код — не глючный код без ооп (иногда он даже с паттернами). Это парадокс такой — концентрирование на жесткости и краткости приводит к гибкости, только вынужденной. Жесткий код — совсем не глючный код. Под жесткостью как раз понимается не то, что его нельзя читать или менять, а то, что в нем максимум ограничений для работы программы ровно в заданном русле.
Вам же, когда надо завести переменную int, не приходит в голову сразу, наперед, дать ей тип double? Ведь в последнем случае код будет более гибким. Double может работать и с целыми числами и если вдруг в будущем понадобится, с плавающей точкой.
Да всё нормально — я не против вообще креатива, потому как его не избежать. Любое решение будет включать в себя долю креатива. Я только за направление стремлений — от креатива подальше. Механические правила рефакторинга, ТДД, выражение мыслей в коде, который пришли от заказчика.
Как можно более прямым способом == как можно меньше от себя креатива.
да хотелось как-то так выразить в художественной форме, чтобы не надо было обосновывать. Шкала перевернута — это главное. А остальное Вы в общем-то знаете. Если складывать разные методы оценки кода по шкале — плохо-хуже, больше градаций получается. Психологически. Хорошо-лучше — не работает. Потому что любое хорошо подойдет.
А про математиков — слышал, не согласен. Это не единственное мнение о математике. Фантазия там нужна, как способность понять и удерживать в голове сложные модели, но не фантазирование. Разные вещи.
Немного сомневаюсь, что автор поста писал такой серьезный транслятор. Так обычно руками надо переписывать :)
Если код преобразован руками, то включается субъективный фактор — умения автора, опыт. Изменение кода может быть настолько серьезным, что будет неизвестно, сам автор нашел ошибки или это следствие статической типизации.
Но если даже простое преобразование позволило найти столько ошибок в коде — это показательно. Вообще, не понятно, как происходило исследование и что это было (каюсь, читать по ссылке более полную информацию — лень). Но пост плюсанул — хорошая тема и комментарии.
Я сторонник падений и не работы в случае ошибки или даже подозрения на нее. Если компилятор нашел ошибку даже в мертвом коде — самое время исправить.
Но это, так, мои субъективные предпочтения.
Я также пишу на J (динамически типизированный язык), который очень нравится. Но там такая типизация оправдана из-за его подхода к вычислениям. Он работает в основном с многомерными матрицами чисел. Сам приводит к нужным типам.
Поэтому, кстати, странное исследование в посте. Если взять код на динамически типизированном языке и просто преобразовать в код статически типизированного языка, то вряд ли от этого он много выиграет и будет использовать статическую типизацию эффективно.
Это так. Но «в которых может находиться программа в не ошибочном состоянии». При статической типизации (если код еще правильно написан), то программа физически не может попасть в ошибочную точку. При динамической как бы попадет, но упадет.
А Вы представьте, что в динамически типизированном языке всегда пишете такими некрасивыми строками. Разве только что без кавычек.
Статическую типизацию в языке лучше воспринимать, как дополнительную возможность сделать код более надежным. Сам язык не гарантирует, что программист будет правильно использовать эту возможность. Вот, код выше показывает, что можно сделать эквивалент динамической типизации.
Код с использование статической типизации надо уметь готовить. Можно представить, что когда программист пытается создать программу, то он определяет множества, интервалы для переменных, в которых может находиться программа в не ошибочном состоянии. Статическая типизация — это мощный инструмент повышения надежности.
Не пользоваться статической типизацией в статически типизированном языке — тоже запросто: можно пользоваться событиями в С# без меры, можно создать класс Message с списком object и передавать и ифами вызывать методы (был такой код в проекте), можно сводить все объекты к базовому, передавать через один метод «чтобы через одну дырку», а там ифами определять тип и приводить к наследнику. Способов много, а умельцев еще больше.
Если Ваш проект — всё приложение, а не библиотека на продажу, то создавать надо только то, что используется. Даже если 1% использует. Если Вы как клиент точно уверены, что Вам нужен объект и он должен быть — вызываете Get. Или даже если не уверены, а подозреваете, что так должно быть. Работа приложения покажет. Надо выбирать более ограниченный вариант.
Потом, механически, в данном случае именно так и подозревалось: после вызова метода идет проверка на null. Как минимум, если в паре мест встречается такая проверка — DRY требует вынести в один метод. И можно даже писать такой метод (с выбрасыванием эксепшина) для поиска объектов по базе, которых заведомо может не быть. Создать свой эксепшин — NotFoundException. Тогда метод будет выглядеть так:
Product Get(int productId)
{
var product = GetIfExist(productId);
if (product == null)
{
throw new NotFoundException(...);
}
return product;
}
Таким образом клиентский код не будет загрязняться проверками. А выброшенное исключение по сути является частью логики ПО, а не ошибкой.
Вообще, null — это вещь плохая и ненужная. Оно по сути означает: ссылка на объект в куче не инициализирована. А то, что это «объект не найден к базе» — за уши притянуто. Null — это тип в типе. По смыслу — тип — это множество возможных значений. А когда метод может возвращать null, то это говорит, что метод может возвратить одно из двух: [Product | null]. Хотелось бы, чтобы если я подразумевал, что что-то не нашел в базе, например, то я бы сам указывал это в коде явно типом: [Product | NotFound]. А так, получается, что кишки устройства дотнет присутствуют везде в коде и везде ведется борьба в коде с возможными ненужными null.
Вообще, мой посыл в комментарии был в том, что код должен выражать смысл задачи. Никакого защитного программирования. Если Вы ожидаете от метода что-то, то ровно это одно метод и должен возвращать, а не неявно подсовывать иногда null. Тогда код остается чистым автоматически. К сожалению, от ссылок null не избавиться языковыми средствами шарпа, поэтому иногда приходится в установках полей писать проверки. Не было бы этого уродства в шарпе, не было бы и там проверок. И отлично, надежно проверял бы корректность программы компилятор, а не появлялись ошибки в рантайме.
А проверять входные параметры, как принято в защитном программировании — не нужно (не обязательно). Если создается объект или в нем меняются поля, то это состояние может сохраняться между вызовами и потом ошибку, кем она внесена, будет тяжело найти. А локальные переменные, в том числе и параметры, не отвечающие требованиям, заставят и так упасть программу сразу же. Проверки захламляют код. Иногда они разве что дают более внятное описание ошибки. Но это не особая проблема. Есть стек. А если бы не было null, так вообще все сообщения об ошибках были более менее читаемыми.
Защитное программирование хорошо в языках, где ошибка может привести к неопределенному поведению, а не падению. В языках, где программно управляете памятью. Вот там, в С++/С, надо опасаться всего.
В статье по сути пришли к этому же, что я написал, как бы убрав почти везде защитное программирование, хотя исходили из него вначале.
А, извините, невнимателен. В статье в конце о том же. Видимо у меня другая терминология. Не рассматриваю это как защитное программирование, а просто считаю, что null — это враг смысла в коде, который зачем-то всегда подается в коробке с классами и от которого нельзя избавиться. Поэтому по умолчанию у меня его нет. Методы не возвращают, если явно в имени это не указано и в этом явный смысл. Защита — это когда на вход методу подается «что-то не то» и мы защищаемся. Я же думаю — на выходе отдавать надо то, что просят, а если не можешь — кинуть исключение. Проверки только в местах сайд-эффектов — конструкторы или установки полей из методов. В общем, в статье с другой стороны это и описали.
Во-первых надо отличать вызов метода и конструктор. Дело в том, что конструктор создает объект, который «хранит состояние» между вызовами. Принципиально надо сводить всё к случаям, чтобы программа не могла в какое-то время находиться в несогласованном состоянии. Т.е. если в конструктор передаются ссылки null, а принципиально не может или не должен быть объект с null-cсылками — программа должна не создать объект, а упасть. Т.е. в конструкторе — да, проверки нужны.
В методе не обязательны. Пусть падает.
Но там говнокод в другом.
var product = this.productRepository.Get(productId);
if (product == null)
throw new InvalidOperationException("Product was null.");
И как думаете, после каждого вызова метода Get надо такие проверки делать? Может лучше внутри Get поместить эту проверку? В Linq есть методы, которые хорошо называются, например: FirstOrDefault — в названии заключено, что ждать от метода. Это очень плохая практика — взять, назвать метод Get, а потом, ожидать, что из него не только объект, но и null возвращается. Если в каких-то случаях нужен будет метод, который возвратит «пустоту» в случае «не нашел», то лучше для этих случаев писать отдельный метод. Но из метода Get ожидать объект. Договориться как-то с названиями методов. Например, позволять null — метод GetIfExists(). А метод Get (или Single, или как договоритесь), обязательно возвращает.
И тогда и код чистый и DRY и смысла в коде больше, уверенности больше. Защитное программирование в общем-то не причем. Пусть падает и падает как можно быстрее. Это и мотив — написать проверки в конструкторе. В методах и так упадет, без разрыва по времени. Просто тяжелее искать, что упало. Но поддержка нормальных имен методов и по возможности вообще исключение null из всех мест, где по смыслу не может быть — это ключ к чистоте и надежности кода.
хороший программист не может повысить свою производительность, потому что он и так работает близко к пику. Овертаймы не считаем, это ненормально — работать по часам выше нормы.
угу. Назад зачем?
Ну да ладно. Под «шагом» я подразумевал строго заданное расстояние. Это и есть субъективный домысел. Т.е. допущение. А значит и ограничение модели.
В программировании я часто встречаю обратное — когда этот интеллект заменяют попугайство-хвастовством. Реально, человек не может код с десяток строк написать, где цвет от строки к строки менялся линейно, но считает дураками всех, кто не знает, что контрл+б в редакторе — что-то делает в обход менюшке. К счастью, таких программистов, которые реально думают, что знать несколько десятков слов и быть в треде — это самое важное — не так много.
С другой стороны, программирование не настолько и простое в сравнении с другими науками. Есть база, computer science — которые не меняются десятилетиями.
Конечно, есть большой пласт программистов, которые целью своей жизни считают прыгание по фреймворкам, мышкой что-то там делание и получение каких-то сайтов или что-то модное даже и не кодить.
Я нигде не утверждаю, что программисту не стоит ничему учиться и держаться за старое. И бояться нового. Но по-моему, и думаю, опытные программисты меня поддержат — новое важно применять, но с оглядкой. Ни в коем случае не согласен, что самое главное — это фичи, даже не смотря на ошибки. Устоявшиеся вещи хоть немного себя зарекомендовавшие надо применять. Риски меньше. Думаете, так только админы думают из страха? Конечно, тормоза придумали трусы.
Может быть у вас собрался такой коллектив, где только такие программисты. Конечно, программист не должен жить только на своем коде и писать велосипеды. Всё, что упрощает деятельность — в копилку. И учиться надо новому постоянно. Другое дело, надо не забывать смотреть на цель — зачем этот проект. Не использовать его для фана за деньги заказчика. Молодые не очень опытные программисты часто этим болеют. Пытаются попробовать всё в проекте, а не дома. Нужно это или нет. Причем, по поводу каждой мелочи. Иногда код написать для мелкой задачи — полстраницы, программист будет изучать неделями новые фреймворки, чтобы всунуть чей-то комбайн.
Вообще, в программировании это болезнь. Я бы сказал, что у программистов очень низкий уровень образования (вот так с плеча), сам преподавал им когда-то. Из-за этого, особенно молодые программисты, считают, что крутость заключается в том, сколько мелочей они услышали и запомнили. Какую-то комбинацию кнопок — и считают, что достигли интеллектуальной вершины. Есть такой тип программистов — поверхностно-обзоро-читатели. Каждый день или через день, они ставят новые фишки домой, почти как девочки в социальных сетях статусы меняют.
Не могут ничего или мало, но говорят обо всём.
Плохой код — это код, в котором не видно такого описания. Конечно, почти нельзя так добиться, чтобы везде в коде было только описание того, что нужно. Но отрицательная шкала оценок кода снимает это противоречие. Код и так плохой. Хуже тот, который не имеет отношения к описанию. Еще более худший код — код, который вообще ничего не описывает. Желательно, чтобы код был читаем локально. Без знаний «архитектуры». Открыв любую страницу кода, хоть там и тысячи страниц, на ней должны читаться намерения.
К сожалению, люди с положительной шкалой скатываются в противоположность. Когда думают, что процент кода небольшой, который прямо решает задачу, а остальное — кирпичики — начинают писать программу ради программы. Это вроде как бы сняли фильм, где во всех подробностях описывается съемка фильма и ничего более. Как фильм, такой может существовать. Процент людей, которым он будет интересен — крайне мал.
А в программировании всё ровно наоборот. Таких бессюжетных фильмов те самые 95 процентов. Программисты увлекаются чем угодно, что касается самого процесса — оптимизациями, многопоточностью, паттернами с гибкостью, распределенной архитектурой. Послушайте о чем они говорят и где лежит сфера их интересов в основном.
Программист, полагая, что без всего этого не обойтись, и имея положительную шкалу оценок — ставит это во главу угла. Получается бессюжетный код.
Математика действительно требует хорошей гибкости ума и внимание, чтобы удержать в уме сложную модель. Но при этом математика — это набор правил, формальных, синтаксических, от которых не отклоняются. Решения задач — это формулы, правила с которыми строго определены. Одна механика.
В посте я больше подразумевал науки, которые изучают природу. Математика в принципе — это аппарат для них.
Но считать заказчика идиотом, даже если это так, плохо. Много раз замечал тенденцию, когда программисты думают, что они лучше знают, что надо заказчику, чем он сам.
Вам же, когда надо завести переменную int, не приходит в голову сразу, наперед, дать ей тип double? Ведь в последнем случае код будет более гибким. Double может работать и с целыми числами и если вдруг в будущем понадобится, с плавающей точкой.
Как можно более прямым способом == как можно меньше от себя креатива.
А про математиков — слышал, не согласен. Это не единственное мнение о математике. Фантазия там нужна, как способность понять и удерживать в голове сложные модели, но не фантазирование. Разные вещи.
Если код преобразован руками, то включается субъективный фактор — умения автора, опыт. Изменение кода может быть настолько серьезным, что будет неизвестно, сам автор нашел ошибки или это следствие статической типизации.
Но если даже простое преобразование позволило найти столько ошибок в коде — это показательно. Вообще, не понятно, как происходило исследование и что это было (каюсь, читать по ссылке более полную информацию — лень). Но пост плюсанул — хорошая тема и комментарии.
Но это, так, мои субъективные предпочтения.
Я также пишу на J (динамически типизированный язык), который очень нравится. Но там такая типизация оправдана из-за его подхода к вычислениям. Он работает в основном с многомерными матрицами чисел. Сам приводит к нужным типам.
Статическую типизацию в языке лучше воспринимать, как дополнительную возможность сделать код более надежным. Сам язык не гарантирует, что программист будет правильно использовать эту возможность. Вот, код выше показывает, что можно сделать эквивалент динамической типизации.
Код с использование статической типизации надо уметь готовить. Можно представить, что когда программист пытается создать программу, то он определяет множества, интервалы для переменных, в которых может находиться программа в не ошибочном состоянии. Статическая типизация — это мощный инструмент повышения надежности.
Не пользоваться статической типизацией в статически типизированном языке — тоже запросто: можно пользоваться событиями в С# без меры, можно создать класс Message с списком object и передавать и ифами вызывать методы (был такой код в проекте), можно сводить все объекты к базовому, передавать через один метод «чтобы через одну дырку», а там ифами определять тип и приводить к наследнику. Способов много, а умельцев еще больше.
Потом, механически, в данном случае именно так и подозревалось: после вызова метода идет проверка на null. Как минимум, если в паре мест встречается такая проверка — DRY требует вынести в один метод. И можно даже писать такой метод (с выбрасыванием эксепшина) для поиска объектов по базе, которых заведомо может не быть. Создать свой эксепшин — NotFoundException. Тогда метод будет выглядеть так:
Таким образом клиентский код не будет загрязняться проверками. А выброшенное исключение по сути является частью логики ПО, а не ошибкой.
Вообще, null — это вещь плохая и ненужная. Оно по сути означает: ссылка на объект в куче не инициализирована. А то, что это «объект не найден к базе» — за уши притянуто. Null — это тип в типе. По смыслу — тип — это множество возможных значений. А когда метод может возвращать null, то это говорит, что метод может возвратить одно из двух: [Product | null]. Хотелось бы, чтобы если я подразумевал, что что-то не нашел в базе, например, то я бы сам указывал это в коде явно типом: [Product | NotFound]. А так, получается, что кишки устройства дотнет присутствуют везде в коде и везде ведется борьба в коде с возможными ненужными null.
Вообще, мой посыл в комментарии был в том, что код должен выражать смысл задачи. Никакого защитного программирования. Если Вы ожидаете от метода что-то, то ровно это одно метод и должен возвращать, а не неявно подсовывать иногда null. Тогда код остается чистым автоматически. К сожалению, от ссылок null не избавиться языковыми средствами шарпа, поэтому иногда приходится в установках полей писать проверки. Не было бы этого уродства в шарпе, не было бы и там проверок. И отлично, надежно проверял бы корректность программы компилятор, а не появлялись ошибки в рантайме.
А проверять входные параметры, как принято в защитном программировании — не нужно (не обязательно). Если создается объект или в нем меняются поля, то это состояние может сохраняться между вызовами и потом ошибку, кем она внесена, будет тяжело найти. А локальные переменные, в том числе и параметры, не отвечающие требованиям, заставят и так упасть программу сразу же. Проверки захламляют код. Иногда они разве что дают более внятное описание ошибки. Но это не особая проблема. Есть стек. А если бы не было null, так вообще все сообщения об ошибках были более менее читаемыми.
Защитное программирование хорошо в языках, где ошибка может привести к неопределенному поведению, а не падению. В языках, где программно управляете памятью. Вот там, в С++/С, надо опасаться всего.
В статье по сути пришли к этому же, что я написал, как бы убрав почти везде защитное программирование, хотя исходили из него вначале.
В методе не обязательны. Пусть падает.
Но там говнокод в другом.
И как думаете, после каждого вызова метода Get надо такие проверки делать? Может лучше внутри Get поместить эту проверку? В Linq есть методы, которые хорошо называются, например: FirstOrDefault — в названии заключено, что ждать от метода. Это очень плохая практика — взять, назвать метод Get, а потом, ожидать, что из него не только объект, но и null возвращается. Если в каких-то случаях нужен будет метод, который возвратит «пустоту» в случае «не нашел», то лучше для этих случаев писать отдельный метод. Но из метода Get ожидать объект. Договориться как-то с названиями методов. Например, позволять null — метод GetIfExists(). А метод Get (или Single, или как договоритесь), обязательно возвращает.
И тогда и код чистый и DRY и смысла в коде больше, уверенности больше. Защитное программирование в общем-то не причем. Пусть падает и падает как можно быстрее. Это и мотив — написать проверки в конструкторе. В методах и так упадет, без разрыва по времени. Просто тяжелее искать, что упало. Но поддержка нормальных имен методов и по возможности вообще исключение null из всех мест, где по смыслу не может быть — это ключ к чистоте и надежности кода.
Ну да ладно. Под «шагом» я подразумевал строго заданное расстояние. Это и есть субъективный домысел. Т.е. допущение. А значит и ограничение модели.