Предлагаю вашему вниманию перевод оригинальной статьи Роберта С. Мартина.
За последние несколько месяцев я попробовал два новых языка. Swift и Kotlin. У этих двух языков есть ряд общих особенностей. Действительно, сходство настолько сильное, что мне стало интересно, не является ли это новой тенденцией в нашей языкомешалке. Если это действительно так, то это тёмный путь.
Оба языка включают в себя некоторые функциональные характеристики. Например, в них обоих есть лямбды. В целом, это хорошая штука. Чем больше мы узнаем о функциональном программировании, тем лучше. Эти языки далеки от по-настоящему функционального языка программирования; но каждый шаг в этом направлении — хороший шаг.
Проблема в том, что оба языка сделали ставку на сильную статическую типизацию. Кажется, оба намерены заткнуть каждую дыру в своём родном языке. В случае со Swift
– это странный гибрид C
и Smalltalk
, который называется Objective-C; поэтому, возможно, упор на типизацию понятен. Что касается Kotlin – его предком является уже довольно строго типизированная Java.
Я не хочу, чтобы вы думали, что я против статически типизированных языков. Я не против. Есть определенные преимущества как для динамических, так и для статических языков; и я с удовольствием пользуюсь обоими видами. Я предпочитаю динамическую типизацию, и поэтому я иногда использую Clojure
. С другой стороны, я, вероятно, пишу больше Java
, чем Clojure
. Поэтому вы можете считать меня би-типичным. Я иду по обеим сторонам улицы — если так можно выразиться.
Дело не в том, что меня беспокоит статическая типизация Swift
и Kotlin
. Скорее меня беспокоит глубина статической типизации.
Я бы не назвал Java
сильно упрямым языком, когда речь идет о статической типизации. Вы можете создавать структуры в Java
, которые хорошо следуют правилам типов; но вы также можете нарушать многие правила типов, когда захотите или вам нужно. Язык немного жалуется, когда вы это делаете, и создаёт соответствующие барьеры для этого, но не так много, чтобы быть обструкционистом.
Swift
и Kotlin
, с другой стороны, становятся абсолютно непреклонными, когда дело доходит до их правил типов. Например, в Swift
, если вы объявите функцию, которая бросает исключение, то каждый вызов этой функции, вплоть до начала древа вызовов, должен быть обёрнут в блок do-try
, или try!
, или try?
. В этом языке нет способа тихо выбросить исключение вплоть до верхнего уровня, без прокидывания через все древо вызовов. (Вы можете посмотреть, как Джастин и я боремся с этим, в наших видеоматериалах Mobile Application Case Study)
Возможно, вы думаете, что это хорошо. Возможно, вы думаете, что в системах было много ошибок, вызванных некорректными исключениями. Возможно, вы считаете, что исключения, которые не сопровождаются, шаг за шагом, вверх по стеку вызовов, являются рискованными и подвержены ошибкам. И, конечно, вы были бы правы. Необъявленные и неуправляемые исключения очень рискованны.
А теперь вопрос. Кто должен разруливать все эти риски? Язык? Или это работа программиста?
В Kotlin
, вы не можете наследоваться от класса, или переопределить функцию, пока вы не отметите этот класс или функцию ключевым словом open
. Вы также не можете переопределить функцию, если она не отмечена ключевым словом override
. Если вы не объявите класс как открытый для наследования, язык не позволит вам наследоваться от такого класса.
Возможно, вы думаете, что это хорошо. Возможно, вы считаете, что иерархии наследования, которым разрешено расти без ограничений, являются источником ошибок и риска. Возможно, вы думаете, что можно устранить целые классы ошибок, заставив программистов явно объявлять их классы открытыми для наследования. И вы можете быть правы. Наследование — рискованная вещь. Что-то может пойти не так, при переопределении функции в классе-наследнике.
А теперь вопрос. Кто должен разруливать все эти риски? Язык? Или это работа программиста?
Оба языка, Swift
и Kotlin
, включают в себя концепцию обнуляемых типов (nullable). Тот факт, что переменная может содержать null
, становится частью типа этой переменной. Переменная типа String
не может содержать значение null
, она может содержать только конкретную строку. С другой стороны, переменная типа String
? имеет обнуляемый тип и может содержать null
.
Правила языка настаивают на том, что когда вы используете переменную, допускающую значение null
, вы должны сначала проверить эту переменную на null
. Так что если s
это String?
тогда var l = s.length
не будет компилироваться. Вместо этого вам следует писать так: var l = s?.length ?: 0
или var l = if (s != null) s.length else 0
.
Возможно, вы думаете, что это хорошо. Возможно, вы видели довольно много NPE в вашей жизни. Возможно, вы знаете, без тени сомнения, что непроверенные null`ы
являются причиной сбоев программного обеспечения на миллиарды и миллиарды долларов. (Действительно, документация Kotlin
называет NPE «Billion Dollar Bug»). И, конечно, вы правы. Очень рискованно иметь неконтролируемые null`ы
повсюду.
А теперь вопрос. Кто должен разруливать все эти null`ы
? Язык? Или это работа программиста?
Эти языки подобны маленькому голландскому мальчику, затыкающему пальцами дырки в дамбе. Каждый раз, когда появляется новая ошибка – в язык добавляется новая функция для предотвращения такой ошибки. И поэтому эти языки накапливают все больше и больше пальцев в дырах в дамбах. Проблема в том, что в итоге закончатся пальцы рук и ног.
Но пока пальцы на руках и ногах не закончились, мы создаём языки, которые содержат десятки ключевых слов, сотни ограничений, извилистый синтаксис и справочное руководство, которое читается как книга закона. Действительно, чтобы стать экспертом в этих языках, вы должны стать юристом по языку (термин, который был изобретен в эпоху C++
).
Это неверный путь!
Спросите себя, почему мы пытаемся исправить дефекты языковыми функциями. Ответ должен быть очевиден. Мы пытаемся исправить эти дефекты, потому что эти дефекты случаются слишком часто.
Теперь спросите себя, почему эти дефекты случаются слишком часто. Если вы ответите, что наши языки не мешают им, я настоятельно советую вам бросить свою работу и никогда не думать о том, чтобы снова стать программистом. Потому что дефекты никогда не являются ошибкой наших языков. Дефекты — это ошибка программистов. Это программисты создают дефекты, а не языки.
И что же программисты должны делать для предотвращения дефектов? Я загадаю вам загадку. Вот пара подсказок. Это глагол. Он начинается на букву «Т». Да. Вы поняли. ТЕСТИРОВАТЬ!
Вы пишете тесты, чтобы ваша система не возвращала неожиданные значения null
. Вы пишете тесты, чтобы ваша система обрабатывала null
во всех входных данных. Вы пишете тесты, чтобы каждое исключение, которое вы можете выбросить, было где-то обработано.
Почему эти языки используют все эти функции? Потому что программисты не покрывают тестами свой код. И поскольку программисты не тестируют свой код, у нас теперь есть языки, которые заставляют нас ставить слово open
перед каждым классом, от которого мы хотим наследоваться. Теперь у нас есть языки, которые заставляют нас оборачивать каждую функцию, сквозь всё древо вызовов, в блок try!
. Теперь у нас есть языки, которые настолько ограничены и настолько переобусловлены, что нужно проектировать всю систему заранее, прежде чем начать кодить.
Рассмотрим пример. Как узнать, открыт ли класс для наследования или нет? Как я узнаю, что где-то вниз по древу вызовов кто-то может выбросить исключение? Сколько кода мне придется изменить, когда я наконец узнаю, что кто-то действительно должен вернуть null
в древе вызовов?
Все эти ограничения, налагаемые этими языками, предполагают, что программист обладает совершенным знанием системы, до того как начать писать её. Они предполагают, что вы знаете, какие классы должны быть открыты для наследования, а какие нет. Они предполагают, что вы знаете, какие вызовы будут генерировать исключения, а какие — нет. Они предполагают, что вы знаете, какие функции будут возвращать null
, а какие нет.
И из-за всего этого есть основания полагать, что они наказывают вас, когда вы неправы. Они заставляют вас вернуться назад и изменить огромное количество кода, добавив try!
или ?:
или open
сквозь всё древо вызовов.
И как вы избегаете этого наказания? Есть два пути. Тот, который работает, и тот, который не работает. Тот, который не работает, заключается в том, чтобы проектировать всё перед написанием кода. А тот, который избегает наказания, должен переопределить все меры предосторожности.
И поэтому вы объявляете все свои классы и все свои функции открытыми для наследования. Вы никогда не используете исключения. И вы привыкаете к использованию большого количества символов !
для переопределения проверок на null
и позволяете NPE расплодиться в своих системах.
Почему атомная станция в Чернобыле загорелась, расплавилась, разрушила небольшой город и оставила большую территорию непригодной для жизни? Они переопределили все меры предосторожности. Так вот, не надо полагаться на безопасность, чтобы предотвратить катастрофу. Вместо этого лучше привыкнуть к написанию большого количества тестов, независимо от того, какой язык вы используете!
Комментарии (75)
snuk182
16.03.2017 16:58+18С трудом дочитал до конца — уровень истерики и субъективизма зашкаливает.
Дефекты — это ошибка программистов. Это программисты создают дефекты, а не языки.
maxru
16.03.2017 17:00+4Языки, в свою очередь, иногда этому способствуют.
Псст, парень, не хочешь объехать этот мучительный участок на потайном велосипеде?
snuk182
16.03.2017 17:24+12А блин, я только заметил — это же мой любимый Роберт Мартин, гений тоталитаризма в софтверном менеджменте. До сих пор при одном упоминании о книге «The Clean Coder» хочется убивать, даже несмотря на то, что там по сути довольно здравые вещи. Но стиль изложения все перечеркивает — собственные
шишкидостижения преподносятся как неотъемлемая часть профессионального роста любого программиста, что позволяет с высоты своего, так сказать, опыта поплевывать на седины измучившихся и жаждущих ценного совета салаг. Ну прямо как на форуме рожениц — девочка, которая «уже», не минует возможности поучить тех, кто только ждет своей участи.
maxru
16.03.2017 16:59+4То есть тезисно получается, что язык программирования что-то должен разработчику/Бобу?
phillennium
16.03.2017 17:18+12Если после текста заинтересовало мнение «другой стороны» — этот пост недавно как раз обсудили в интервью на Хабре Андрей Бреслав, возглавляющий Kotlin, и Антон Кекс, пишущий на Kotlin:
«Антон: Андрей, а ты видел интереснейший пост Uncle Bob как раз на тему Kotlin и nullability? Он хорошо прошёлся по этой nullability, мне интересен твой комментарий.
Андрей: Он написал в общей сложности три поста, посыл примерно такой: когда-то были языки без типов, потом языки с типами, потом снова стали популярны языки без типов, такой маятник качается туда-сюда, сейчас маятник качнулся в сторону типов и ушёл за точку баланса. У нас больше типов, чем надо.
Во-первых, это предполагает, что Uncle Bob знает, где точка баланса. Ну хорошо, он знает, а я думаю, что я тоже знаю, и у меня другое мнение. В этом месте можно просто обменяться мнениями.
Во-вторых, там есть некоторые конкретные аргументы «как же, если я, например, сделал какой-нибудь тип в своей программе nullable, то мне ж теперь надо поменять всю остальную программу, чтобы эта программа скомпилировалась». Этот момент я не понимаю, потому что — да, конечно, нужно что-то поменять, это содержательно. Если какой-то тип стал nullable, значит, код, который этот тип использует, должен учесть этот факт, иначе он будет неправильно работать! И, конечно, этот код надо поменять. Ну, можно его запустить, получить исключение, и поменять потом, а можно просто сразу поменять. Вот в Kotlin надо сразу.
Там ещё есть какие-то аргументы, аналогии с const в C++, ещё чем-то — эта аналогия не совсем корректная, по-моему.
Антон: При всём уважении к Uncle Bob, мне тоже кажется, что он просто использовал возможность ещё раз сказать «вы всё равно должны писать тесты для своего кода, и компилятор вас не спасёт». В чём он, в принципе, прав, но мне лично очень нравится в Kotlin эта фича с nullability, может быть, для меня это даже одна из основных фич, ради которых хочу писать на Котлине. С другой стороны, бывает такое, когда Kotlin не позволяет мне легко описать то, что я хочу, компилятор говорит, что я должен где-то поставить либо ?, либо !!.. В последнее время, когда у меня всё больше опыта с языком, мне всё меньше приходится бороться с компилятором, но есть такие кейсы. Для этого есть ещё ключевое слово lateinit, которое иногда помогает. Так что есть и плюсы, и минусы, но мне кажется, что всё-таки уклон ушёл в правильную сторону, что от этого больше пользы, чем неудобства.
Андрей: Безусловно, я согласен, что минусы есть, но за всё надо платить. Если мы хотим, чтобы компилятор что-то гарантировал, то требуется какое-то количество работы с нашей стороны. Здесь просто вопрос, что более оправданно. По-моему, опыт нас самих и всех остальных людей с Kotlin показывает, что введение nullable-типов вполне оправдано, получилось хорошо. »Nakosika
16.03.2017 18:59-5Анкл Боб вполне справедливо не хочет переписывать пол программы когда переменная стала nullable. Скорее всего она стала nullable только в какой-то определенный промежуток времени (к примеру во время инициализации), а вообще там нул можно не учитывать. Проверка на нул нужна только если мы пишем ifelse на основании того есть там какие-то данные или нет. В других случаях это синтаксический шум, который будет скрывать нулы, и провоцировать ошибки которые тяжело найти.
guai
16.03.2017 19:21+1в котлине есть lateinit как раз для этого
AlexTheLost
16.03.2017 20:36В статье есть и на это ответ :)
Но пока пальцы на руках и ногах не закончились, мы создаём языки, которые содержат десятки ключевых слов, сотни ограничений, извилистый синтаксис и справочное руководство, которое читается как книга закона
Я за Option.)
guai
16.03.2017 23:55+2с другой стороны, а куда мне еще девать пальцы, солить?
какая-то высшая цель есть, чтоб их не использовать?
вы за опшен, ваш коллега против, и будете вы в свои опшны нуллы получать :)
или можно потратить палец и решить проблему кардинальноsolver
17.03.2017 10:39+1Так о том-то и речь собственно, что проблема кардинально не решена.
Просто пальцем заткнули дырку…
Guzergus
17.03.2017 06:16+3Не совсем понимаю, с чего бы переменной на пустом месте становится nullable. Если это изменение в бизнес-логике — часть кода придётся переписать, от этого не уйти. Если это изменения в используемых библиотеках — можно либо остаться на старой версии, либо переписать код. Как правило, над обратной совместимостью тоже думают и не будут менять тип переменной туда-сюда. Но если уж изменили — нужно переписать код, и это не проблема nullable.
Других сценариев «переменная взяла и стала nullable» я, честно говоря, не вижу.
reforms
16.03.2017 17:25Я не первый раз слышу про проблемы Kotlin и ?: и даже местами понимаю автора. Здесь вот интересно, можно ли провести аналогию с Generic в Java — определенную избыточность на уровне синтаксиса, особенно до появления Diamond Operator? Просто я помню свои возмущения когда приходилось писать полностью Map<String, String> someName = new HashMap<String, String>(); Мне таки казалось эта конструкция невозможной многословностью — ведь я сам знаю, что пишу!
yarric
16.03.2017 17:26-4Из статьи больше всего понравилась ссылка на языкомешалку. Это действительно проблема, поскольку, несмотря на то, что новые языки не добавляют почти ничего концептуально нового по сравнению с языками 70-х — 80-х годов, они всё-же разняться в множестве мелких деталей и требуют много времени на освоение. Лучше бы это время потратить на программирование или создание новых подходов к программированию, как это делается для того же JavaScript, даже для старой версии которого было написано немало оригинальных фреймворков.
snuk182
16.03.2017 18:00+24C++ — самая наглядная демонстрация кривости подхода «старым языкам — новые подходы».
Пока вы один, вы можете писать самый чистый и идеологически верный код в мире, но стоит команде расшириться, начинается пыщь пыщь ололо. Пойдут линты, хуки на коммиты, тренинги, в крайних случаях придется ставить грязный, вонючий, весь в солидоле костыль, минуя все хуки и линты, потому что прод упал, а возиться с идеологией некогда. Рано или поздно придет кто-то не знающий о правилах поведения и начнет совать код в стиле «я так тридцать лет пишу, что вы меня учите?». В то же время запрет городить фигню на уровне компилятора резко сужает необходимость распыляться на мелочи и больше внимания уделить тому, что ты, собственно, хочешь сделать.yarric
16.03.2017 22:28С C++ проблема в том, что каждый может программировать на каком-то его подмножестве, да и в принципе у него нет какого-то определенного дизайна, просто нагромождение фич.
develop7
16.03.2017 18:08+2новые языки не добавляют почти ничего концептуально нового по сравнению с языками 70-х — 80-х годов
define «почти ничего» и «языки 70-80гг»
yarric
16.03.2017 23:39-2В современных языках трудно найти какую-либо существенную фичу, которой меньше 30 лет. Отличия языков друг от друга, и от более "старых" языков всё больше сводятся к вкусовщине вроде нюансов наименований ключевых слов и синтаксическому сахарку.
develop7
16.03.2017 23:44+3Это я уже понял. Так какие именно языки вы имеете в виду? Приведите характерных примеров.
yarric
17.03.2017 08:50+1Примеры чего? Давно реализованных фич? ООП, корутины, сборка мусора — Simula-67, 1967 год. Исключения — PL/I, 1964 год. Функциональное программирование — LISP, 1958 год.
develop7
17.03.2017 10:36+1Лихо вы всё функциональное программирование лиспом ограничили. Ну ладно, а pattern matching?
yarric
17.03.2017 14:00+1ML, NPL, SNOBOL — 60-е — 70-е годы.
0xd34df00d
18.03.2017 09:19+3Зависимые типы?
yarric
23.03.2017 00:39Nuprl, в 80-х появился, сама теория ещё в 30-х. Не сказал бы, что это сильно распространённый подход в современных языках.
samsergey
23.03.2017 01:57+3Настоящие и стОящие теории требуют вдумчивой проверки временем и длительного осмысления, после чего они превращаются в непотопляемый и неподвластный моде фундамент. Развитие современных ЯП состоит в создании эффективных механизмов для воплощения фундаментальных идей и в поиске современных задач, решаемых "старыми" методами. Изоморфизм Карри-Ховарда, в конце концов, сводит большинство "новых" подходов в программировании к "старым" подходам в алгебре, логике или топологии. Но это не значит, что всё уже "украдено до нас" и "всё в географии открыто".
Кстати, находка по-настоящему удачного синтаксического сахара к полезной, но неудобоваримой теоретической конструкции — это очень важная составляющая прогресса. Изобретение бра и кет векторов для исчисления состояний, оператора Гамильтона для представления дифференциальных операторов в теории поля, диаграмм Фейнмана для описания механики частиц, регулярных выражений для представления конечных автоматов, введениеdo
-нотации для монад и адаптация коалгебры для комонады косостояния в виде изящных линз в Haskell, "толстые" стрелки для представления громоздких делегатов… все эти синтаксические нововведения не являются чем-то фундаментальным, но они позволяют просто и последовательно рассуждать о сложных концепциях, не теряя внутренней структуры концепции. Это сродни открытию нового представления для группы или построения изоморфизма между абстрактной "невидимой" структурой и житейским "обозримым" понятием. Это классная, красивая, хоть и непростая и небыстрая часть прогресса!
sbnur
16.03.2017 18:11Последний абзац, скажем так, неверен — проблема Чернобыля не в переопределении мер безопастности, а в том. что персонал станции плохо понимал физику процесса — в большей степени они были теплотехниками, а не ядерщиками.
И кстати они нарушили основную меру безопасности — минимальное число управляющих стержней в реакторе
Но в целом идеи статьи соответствует — если специалисты, применяющие языкы программирования, слабо ими владеют, то меры предосторожности им не помогут. Сильных программистов они будут раздражать.WinPooh73
16.03.2017 23:18+1Одной из проблем Чернобыля была такая конструкция реактора, в которой при введении стержней в зону на некотором участке их хода реактивность не падала, а возрастала… Кто ещё не читал — всем рекомендую Дятлова, физика процесса там подробно описана.
sbnur
16.03.2017 23:55при чем тут конструкция — как я написал была потеряна управляемость реактора — если машину на скользком участке заносит от неудачного маневра, то виновата конструкция?
Кстати, Смоленская АЭС с реактором РБМК, как на Чернобыле, в 2011 году получила высокую оценку экспертами МАГАТЭnetch80
19.03.2017 09:38+2если машину на скользком участке заносит от неудачного маневра, то виновата конструкция?
Если у неё есть компонент, который при начале заноса работает на его усиление — да, виновата конструкция. Это хороший аналог той диверсии в конструкции стержней.
Кстати, Смоленская АЭС с реактором РБМК, как на Чернобыле, в 2011 году получила высокую оценку экспертами МАГАТЭ
После Чернобыльской аварии все РБМК подверглись коренной переработке управляющих конструкций и пересмотру правил эксплуатации.
sbnur
19.03.2017 09:46насчет диверсии это слишком — конечно недостатки есть в любом промышленном объекте, причем исходная разработка была направлена не на мирный атом.
Но несоблюдение правил и непрофессионализм в том смысле о котором я сказал — и есть причина аварии
Кстати аналогичная ава рия была ранее в США на станции Тримайлайленд — где тоже причиной были ошибки персоналаnetch80
19.03.2017 15:29насчет диверсии это слишком — конечно недостатки есть в любом промышленном объекте, причем исходная разработка была направлена не на мирный атом.
Именно. Перевод на "мирные рельсы" без адекватного обеспечения (хотя и начальная разработка в таком опасном виде).
Но несоблюдение правил и непрофессионализм в том смысле о котором я сказал — и есть причина аварии
Неотъемлемая компонента причины, вместе с проблемами конструкции.
sbnur
19.03.2017 15:52-1Нет проблем в конструкции — эти реакторы продолжали выпускать — и они работают на ряде станций.
То есть если на одном автомобиле кто-то разбился, не справившись управлением, то виновата не конструкция.
Все остальное про недостатки конструкции просто досужие рассуждения
Скажите какие конкретно были недостатки, если такие реакторы продолжают эксплуатироваться
sbnur
19.03.2017 09:53и какие были закрыты или переделаны —
Кстати вы представляете себе, что такое переделать действующий реакторnetch80
19.03.2017 15:22Кстати вы представляете себе, что такое переделать действующий реактор
Представляю, примерно. Насколько может представлять это человек технического образования другой специальности. Конечно, это огромные затраты и объём работ. Но последствия у только одной аварии — больше.
Но, если учесть, что те же стержни вообще планово менялись, то эта работа и так должна была быть сделана. Тут больше административные и документальные проблемы.sbnur
19.03.2017 15:46Плохо представляете, несмотря на техническое образование — вот на Чернобыле также думало руководство — инженер — значит пусть работает, а то, что нужно более продвинутая подготовка.в области ядерной физике, то это просто ерунда. Вот смена и смотрела на реактор, как на кастрюлю с кипчящей водой — убавил — потише стало кипеть, добавил — посильнее.
Самое печальное, что вины на них нет — нарушение эксплутационных условий в силу недостаточной подготовки.
Да — кстати причем тут стержни — какая работа должна быть сделана — их нужно добавить?, то есть сделать новые каналы — или как? Напомню в действующем реакторе
Кстати на последующих моделях РБМК модификация заключалась в увеличении мощности от РБМК1000 к РБМК1500 и так далее
t-nick
24.03.2017 12:58Проблема была в том, что конструктора реактора отрицали наличие в нем «уязвимости». На момент аварии о ней никто толком не знал, несмотря на то, что она себя уже проявляла ранее.
sbnur
25.03.2017 10:01Вы, думается сами понимаете, что не существует в принципе неуязвимых конструкций, и этого ни один конструктор и ни один инженер утверждать не будет — это журналистские бредни
Любая конструкция может разрушится в силу случайного или преднамеренного стечения обстоятельств
Бык надежен, пока у него в носу кольцо.
Как я писал подобная авария была прежде на америкаской АЭС, разница только в последствиях — урон от Чернобыля выше (значительно) — американцам повезло. Но результат один блок — находится под саркофагом
really4g
24.03.2017 12:58Согласно информации (и не только этого источника) РБМК после Чернобыля, на станциях, оснащенных реакторами РБМК, были осуществлены мероприятия по модернизации, с целью повышения надежности и безопасности. И как мне видится Смоленская АЭС именно поэтому получила высокую оценку. Я думаю, что реактор Чернобыльской АЭС вряд ли бы сейчас получил оценку подобную Смоленской АЭС.
sbnur
25.03.2017 10:13Ваша ссылка не показывает принципиальные изменения — просто ужесточен контроль, который был бы излишен, если бы персонал был достаточно хорошо подготовлен как специалисты в области физики ядерных реакторов —
Эти все организационные меры так называемая защита от дурака — а нужно ли дурака ставить на управление реактором
guai
16.03.2017 20:29Все классы делать final по дефолту — действительно странная фича котлина. Ни разу не встречал траблов с тем, что возможность наследования несет проблемы. Редко сталкивался с тем, что ты мог бы сделать что-то проще, через наследование, если б автор кода не распихал бездумно файналы где ни попадя, когда предпосылок к этому особо и не видно, начитавшись где-то, что это великое благо. Приходилось доставать сорцы и патчить их.
kolipass
17.03.2017 10:12+1В таких случаях приводят следующий пример: можно унаследоваться от ArrayList и сделать такой, при добавлении нового элемента, будет добавлять его 2 раза.
Теперь следите за руками. Тест, который выполнялся для родителя:
List<Integer> list = new ArrayList<>(); list.add(1); assert list.size()==1;
не будет работать у потомка:
List<Integer> list = new DoubleList<>(); list.add(1); assert list.size()==1;
Ну и, соответственно, если кто-то подложит в реализацию вместо ArrayList, DoubleList то логика в приложении пойдёт не по задуманным рельсам.
sAntee
17.03.2017 10:26А это, насколько я понимаю, «фича» джавы — что любой метод, если не сказано обратное, виртуальный. В C# и C++ вот, например, не так, и ничего, живем.
DarkEld3r
17.03.2017 11:10Дык, разве против этого не такой же самый аргумент будет? Мол как всё final по умолчанию неудобно, так и отсутствие виртуальности (по умолчанию) тоже неудобно.
И я действительно встречал людей, которые считают, что (в С++) лучше писать virtual для каждой функции: мол так удобнее и гибче, а то вдруг автор оригинального класса что-то не продумал, а поменьять поведение в наследники уже нельзя. Переубедить не удалось.
guai
17.03.2017 12:18+1Вот как раз такого я и не видел никогда в реальном коде.
Я вполне понимаю, почему класс Class финальный. ArrayList кстати не финальный, но даже если бы и был, я бы, наверное, подумал, что была причина. А вот зачем всё подряд делать финальным — убей не понимаю.
Это уже моя ответственность сделать так, чтоб мои наследники ничего не ломали. Зачем у меня вообще отбирать возможность даже попытаться сделать правильно?
И конкретно для котлина это выливается в невозможность юзать некоторые явавские либы, которые генерят своих наследников с какой-то там своей дополнительной функциональностью, без того, чтобы везде натыкать open class.kosmos89
17.03.2017 13:46А вот зачем всё подряд делать финальным — убей не понимаю.
Возможно, для оптимизации.
snuk182
18.03.2017 00:07Для «Composition over Inheritance».
kosmos89
18.03.2017 00:23Отсутствие final не мешает делать композицию.
snuk182
18.03.2017 00:41Я к тому, что наследование сейчас считается дурным тоном, так как толку от него не столько, сколько головной боли при архитектуре классов. Проще запретить, чем объяснять, как безопасно.
Субъективно — в моем окружении джависты не страдают, поскольку с младых ногтей обучены оперировать интерфейсами и композицией вместо абстрактных классов. За прям всех, конечно же, не скажу.
Beholder
18.03.2017 13:59Есть плагин Kotlin компилятора, который классы делает open по умолчанию. Или все, или помеченные определёнными аннтоциями (например, для Spring).
samsergey
16.03.2017 22:19+5А теперь вопрос. Кто должен разруливать все эти риски? Язык? Или это работа программиста?
А кто должен писать тесты? Язык? Или это работа программиста?
Строгая, стройная система типов без «нельзя, но если очень хочется, то можно» даёт возможность использовать системы автоматической генерации тестов, типа QuickCheck, что позволяет упростить работу программиста.
s_suhanov
17.03.2017 00:12+5Истерика про опциональные типы особенно доставила. Жаль, что не упомянули о том, что таким образом на 99.9% снижается вероятность краша приложения. Но разве это важно? )))
i_user
17.03.2017 09:26+7Такое ощущение, что человек написавший это, при всем уважении, не совсем понимает что такое Optional type.
Это не про NPE, а про, черт возьми, настоящую строгую типизированность. Когда сущность какого-то типа обозначает именно сущность этого типа, а не «Стринг — это, конечно, Стринг, но иногда еще и null».
Я не знаю кейса, когда из-за этого не хватило какой-то гибкости языка.
P.S.
А теперь вопрос. Кто должен разруливать все эти риски? Язык? Или это работа программиста?
Работа программиста — фичи деливерить, а не выдавать код нравящийся кому бы то ни было. Если какая-то функциональность позволяет деливерить фичи быстрее с более высоким уровнем качества, не повышая при этом существенно порог входа в технологию (возможно при этом мешая нескольким фрикам делать какую-то жесть) — эта функциональность хорошая.Johan
17.03.2017 12:59+1Вот, да. Эта истерика напоминает лозунги луддитов, но те работы лишались, у них был весомый повод.
snuk182
17.03.2017 14:01+1Тут тоже не очень далеко от правды, имхо: больше дисциплины при программировании — меньше проблем в работе — меньше проблем в синхронизации участников команды — меньше поводов обращаться к «консультантам по аджайлу и ТДД».
kosmos89
17.03.2017 14:07+1А теперь вопрос. Кто должен разруливать все эти риски? Язык? Или это работа программиста?
Ну пусть пишет на азме. Это же его работа...
Спросите себя, почему мы пытаемся исправить дефекты языковыми функциями.
…
И что же программисты должны делать для предотвращения дефектов? Я загадаю вам загадку. Вот пара подсказок. Это глагол. Он начинается на букву «Т». Да. Вы поняли. ТЕСТИРОВАТЬ!А как по мне, так это тестирование является костылем. Причем автору лень писать формальную спецификацию, и предлагает разменять формальную верификацию на ручное, все равно неполное, тестирование. Так где тут выгода, если все равно что-то придется дополнительно писать?
Чернобыль. Тесты.
К чему он сюда Чернобыль припел вообще? Что он там предлагает тестировать? Персонал? Станция и так уже оттестирована и для нее написаны различные инструкции по эксплуатации, которые если не нарушать, ничего и не взорвется.
netpilgrim
24.03.2017 12:58Причем автору лень писать формальную спецификацию, и предлагает разменять формальную верификацию на ручное, все равно неполное, тестирование.
Боб — пропагандист TDD. Очень маловероятно, что речь была про ручное тестирование.
Danik-ik
17.03.2017 20:27Присоединяюсь. Писать много тестов на граммотное использование нулла вместо того, чтобы воспользоваться готовыми тестами, встроенными в компилятор мне кажется неразумным. Ибо DRY.
Final по умолчанию я всё же не понимаю, но паники он у меня не вызывает. Если я не могу унаследоваться от чужого класса — никто не мешает использовать вместо наследования композицию — обернуть его фантиком и проксировать интерфейс, переопределив, что надо, в обёртке. Неидеально из-за несовместимости с исходным классом, но тут уж интерфейсы — наше всё.
Koyotter
17.03.2017 22:44+2Читаю вот я, программист-атомщик, комментарии, и у меня такое чувство что практически все засветившиеся, включая дядю боба, атомщики и все знают про Чернобыль и из-за чего беда произошла...
HKA
24.03.2017 12:59+1Если бы не авторитет первоисточника, я бы подумал, что это очередной вброс. Воспринимается примерно как «выбор инструмента в работе не важен, виноват программист». Угу, даже дворники перешли от метлы к механизации, чтобы повысить производительность и качество.
NaHCO3
> Потому что программисты не покрывают тестами свой код
Тьюринг полнота намекает, что покрыть тестами ВЕСЬ свой код невозможно. А вот если взять некоторое ограниченное подмножество от языка, то для него можно показать корректность. Именно этим и занимается статическая типизация. Доказывает, что некоторые элементы программы, которые целиком укладываются в это подмножество — корректны. Конечно, у вас есть опасные связи между этими островками безопасности, которые нарушают гарантии, но вы знаете где именно вам надо пытаться искать ошибки тестами — на границе!
> Они предполагают, что вы знаете, какие классы должны быть открыты для наследования, а какие нет.
Начните с final. По мере необходимости можете final снимать. Но вообще final очень полезная штука для escape analysis.
DexterHD
Вы это Дяде Бобу объясняете?