7. Не пытайтесь чинить код и пилить фичи самостоятельно. Вы можете писать код только в одном случае, если надо разрулить конфликт между разработчиками. Всё.
Это прям из разряда «вредных советов». Такими темпами можно за два-три года растерять все технические навыки и превратиться в того самого pointy-haired boss.
Зато есть материалы, показывающие, что такой корреляции нет — я даже в статье привёл
Значительная часть вашей статьи опирается на это исследование, однако:
Эта статистика только по тем, кто смог пройти собеседование, т.е. уже набрал какой-то минимальный балл. Если бы всех заваливших собеседование брали на работу и делали performance review через полгода-год, возможно статистика была бы совсем другой и корреляция нашлась бы.
Сами performance review носят очень большую долю случайности и представляют собой огромную проблему в плане объективности процесса. Может быть, даже большую проблему, чем собеседования. По каким-либо формальным метрикам оценить производительность работника практически невозможно (не считать же количество закрытых тикетов или, боже упаси, строк кода), и все сводится к субъективной оценке твоим начальником. В итоге получается огромный разброс от команды к команде. А как известно, случайные данные не будут коррелировать ни с чем. В нашей компании вообще из-за этого отказались от формальных performance review с выставлением числовой оценки сотруднику.
Так что, я не думаю, что из этого ислледования можно сделать вывод, что собеседование никак не предсказывает будущую производительность труда.
Популярно нынче стало хвастаться отсутствием знаний, объясняя свою позицию возможностью загуглить и найти всё, что нужно.
Гугление работает, только когда вы точно знаете, что ищете. Например, как выйти из Vim или как собрать ядро Linux из исходников.
Но часто бывает так, что вы просто не можете знать заранее, что искать. В таких случаях наличие определенных знаний и интуиции может ускорить работу на порядок, буквально делая вас «10x developer»-ом. Вот простой пример из мира Java. Представьте, что вам надо исправить некий неуловимый баг в большом проекте. В процессе чтения кода вам попадается такой кусок:
Map<byte[], String> stringsByMd5Hash = new HashMap<>();
Если вы обладаете тем знанием, что массивы в Java имеют «поверхностные» equals() и hashCode(), проверяющие лишь адрес массива, а не его содержимое, то вы сразу заподозрите неладное. Например, такой код не будет работать:
stringsByMd5Hash.put(new byte[] { 1, 2, 3 }, "foo");
// Не вернет "foo", поскольку в качестве ключа передан другой массив
stringsByMd5Hash.get(new byte[] { 1, 2, 3 });
Без знания этой тонкости языка вы просто не обратите внимания на эту строку и гугление здесь никак вам не поможет. В результате вам может понадобиться несколько часов удаленного дебага в продакшене. Таких примеров можно придумать сколько угодно, для любой области и технологии.
Вы неправы. Константный множитель в показателе — это не то же самое, что константный множитель.
Например, 2^n = o(3^n) («o» малое), поскольку (2^n/3^n) -> 0 при n->\infty. Иными словами, 3^n растёт сильно быстрее, чем 2^n :) Таким образом, 2^n = O(3^n), но 3^n не O(2^n).
С логарифмами — да, основание неважно: log_a(n) = ln(n)/ln(a) = C * ln(n).
Хитрые структуры данных с нетривиальными зависимостями, не укладывающимися в концепцию scoped-based ownership. Например, для реализации текстового редактора могут понадобиться:
— список блоков текста, представленных указателями на внешние буферы;
— деревья типа rope/cord для быстрого поиска по номеру строки;
— индексы для подсветки синтаксиса и т.п.;
— список истории undo, также хранящий указатели на текст во внешних буферах;
— и так далее.
Все это может быть переплетено между собой и хранить ссылки друг на друга.
Вы видимо никогда случайно не добавляли элементы в вектор, по которому в это время итерировались.
Было дело в молодости. Думаю, подобные проблемы можно решать на уровне статического анализатора.
И не забывали инициализировать unique_ptr
Эту проблему можно было бы решить, запретив нулевые указатели и убрав из unique_ptr конструктор по умолчанию, требуя тем самым, чтобы unique_ptr всегда содержал действительный указатель. Если нужно представить отстутствие указателя, то на помощь должна прийти система типов: optional<unique_ptr>. Здесь в Rust все сделали правильно.
И не делали случайное обращение к шареному ресурсу без синхронизации.
От проблем с шареными ресурсами и сихнронизацией вас ни один компилятор не спасет.
А мне не понятно, где вы увидели претензии к unsafe. Я вообще считаю, что Rust отлично спроектирован для поставленных целей, просто мне не вполне близки эти самые цели, поставленные разработчиками языка.
Я лишь хотел сказать, что для каких-то нетривиальных вещей зачастую приходится прибегать к unsafe. А на системном языке писать нетривиальные вещи как раз приходится часто. Получается, что основное заявленное достоинство языка — безопасность памяти — на практике не особо работает.
Я надеюсь, что большинства программистов все же более прагматичный подход. Помимо safe-defaults хорошо бы иметь safe все остальное (на сколько это возможно)
… меньше задумываться о железе, а больше решать проблемы которые перед ними стоят
Для этого есть Java — оличный язык.
Я не хочу чувствовать себя джедаем, просто когда я пишу на системном языке, мне не хочется, чтобы компилятор вставлял мне лишние палки в колеса. Параноидальная проверка лайфтаймов все равно не обеспечит 100%-й безопасности, но значительно усложнит процесс написания и модификации кода. По-моему, мой подход весьма прагматичен. Я просто хочу сделать свою работу, а не ковыряться со всякими
<'a>,
просто чтобы доказать компилятору, что и так вполне очевидно программисту.
В Rust прежде всего напрягает паранойя компилятора и постоянная возня с лайфтаймами, а также отсутсвтие исключений.
Из-за первого, например, на Rust нельзя реализовать двусвязный список без использования unsafe или лишнего оверхеда. Ну или бинарное дерево, где узлы хранят сслыки на родителей. Еще в Rust из-за требований к безопасности памяти необходимо проверять гранциы массивов в рантайме при обращении по индексу. В системном языке все это напрягает.
Я готов пожертвовать 100%-й безопасностью памяти в обмен на отсутствие лишней возни и оверхеда. То есть в моем списке требований расту не хватает той самой бритвенной остроты. На том же C++ можно писать безопасный код довольно просто, соблюдая определенную дисциплину. Только хочется иметь «safe defaults». Например, убрать из языка оператор new (за исключением placement new), конструктор копирования по умолчанию и т.п.
Сделали бы уже нормальный системный язык наконец:
— с ручным управлением памятью и детерминированными деструкторами;
— бритвенно острый, как C++;
— но с нормальным поведением по умолчанию, чтобы было меньше способов порезаться;
— с современной системой типов как в Rust.
Чувствую, не дождусь. Придется самому сделать, когда выйду на пенсию.
Не все биржевые сервера — это high frequency trading. Если это сервер, именно обрабатывающий транзакции, а не скупающий акции ровно в момент их выставления на продажу, то зачем там микросекундный отклик?
По-моему, вы перегнули палку. Совершенно ясно, что не нужна отдельная функция для создания ArrayList'а.
Конкретно в вашем примере может иметь смысл вынести логику преобразования элемента в отдельную функцию. А для преобразования массивов в Java 8 есть стандартные средства.
public void requestSomethingParseAndSave(String parameter) {
List<Integer> data = this.getAPI().getData(parameter);
this.getObservers().notify('data received')
List<Integer> newData = data.stream().map(MyClass::parseData).collect(Collectors.toList());
this.getObservers().notify('data parsed', newData)
this.getAPI().saveData(newData);
}
private static int parseData(int x) {
return x + 15;
}
В чем смысл билдера, если ваш класс и так содержит сеттеры для полей? Билдер имеет смысл для immutable-классов (т.е. когда все поля final) с большим количеством полей, чтобы не вызывать конструктор с тысячей параметров. Если есть сеттеры, почему бы не использовать сам объект вместо билдера?
Но, по-моему, корень вашей проблемы в использовании наследования (которое, из моего скромного опыта, является верным решением менее чем в 1% случаев). Может быть, в вашем случае можно применить композицию вместо наследования?
Касательно приведенного кода: по-моему, вместо хранения ссылки на returnBuilder можно просто использовать (RetBuilder) this, если добавить ораничение RetBuilder extends BuilderImpl и подавить предупреждение об unchecked cast.
Единственное, что в bc раздражает, — это скудность математической библиотеки. Например, возведение в дробную степень отсутствует: $ echo '2^1.5' | bc -l
Runtime warning (func=(main), adr=9): non-zero scale in exponent
2
Приходится извращаться с помощью экспоненты и логарифма, что не очень удобно: $ echo 'e(l(2)*1.5)' | bc -l
2.82842712474619009755
Или какой-нибудь арккосинус если нужно посчитать, приходится использовать арктангенс.
Статья заставила улыбнуться. Снова эти хипстеры со своими «микросервисами» и инновационными методами разработки. Наверняка делают какие-нибудь бесполезные мобильные приложения и не имеют отношения к реальной индустрии ПО. Если в проекте пара миллионов строк кода, то только как следует въехать в архитектуру и структуру кода займет три месяца, и никакие «микросервисы» не помогут (наоборот, придется еще больше информации в голове держать).
Так что, я не думаю, что из этого ислледования можно сделать вывод, что собеседование никак не предсказывает будущую производительность труда.
Гугление работает, только когда вы точно знаете, что ищете. Например, как выйти из Vim или как собрать ядро Linux из исходников.
Но часто бывает так, что вы просто не можете знать заранее, что искать. В таких случаях наличие определенных знаний и интуиции может ускорить работу на порядок, буквально делая вас «10x developer»-ом. Вот простой пример из мира Java. Представьте, что вам надо исправить некий неуловимый баг в большом проекте. В процессе чтения кода вам попадается такой кусок:
Если вы обладаете тем знанием, что массивы в Java имеют «поверхностные» equals() и hashCode(), проверяющие лишь адрес массива, а не его содержимое, то вы сразу заподозрите неладное. Например, такой код не будет работать:
Без знания этой тонкости языка вы просто не обратите внимания на эту строку и гугление здесь никак вам не поможет. В результате вам может понадобиться несколько часов удаленного дебага в продакшене. Таких примеров можно придумать сколько угодно, для любой области и технологии.
Например, 2^n = o(3^n) («o» малое), поскольку (2^n/3^n) -> 0 при n->\infty. Иными словами, 3^n растёт сильно быстрее, чем 2^n :) Таким образом, 2^n = O(3^n), но 3^n не O(2^n).
С логарифмами — да, основание неважно: log_a(n) = ln(n)/ln(a) = C * ln(n).
— список блоков текста, представленных указателями на внешние буферы;
— деревья типа rope/cord для быстрого поиска по номеру строки;
— индексы для подсветки синтаксиса и т.п.;
— список истории undo, также хранящий указатели на текст во внешних буферах;
— и так далее.
Все это может быть переплетено между собой и хранить ссылки друг на друга.
Эту проблему можно было бы решить, запретив нулевые указатели и убрав из unique_ptr конструктор по умолчанию, требуя тем самым, чтобы unique_ptr всегда содержал действительный указатель. Если нужно представить отстутствие указателя, то на помощь должна прийти система типов: optional<unique_ptr>. Здесь в Rust все сделали правильно.
От проблем с шареными ресурсами и сихнронизацией вас ни один компилятор не спасет.
Я лишь хотел сказать, что для каких-то нетривиальных вещей зачастую приходится прибегать к unsafe. А на системном языке писать нетривиальные вещи как раз приходится часто. Получается, что основное заявленное достоинство языка — безопасность памяти — на практике не особо работает.
Я не хочу чувствовать себя джедаем, просто когда я пишу на системном языке, мне не хочется, чтобы компилятор вставлял мне лишние палки в колеса. Параноидальная проверка лайфтаймов все равно не обеспечит 100%-й безопасности, но значительно усложнит процесс написания и модификации кода. По-моему, мой подход весьма прагматичен. Я просто хочу сделать свою работу, а не ковыряться со всякими
просто чтобы доказать компилятору, что и так вполне очевидно программисту.
Из-за первого, например, на Rust нельзя реализовать двусвязный список без использования unsafe или лишнего оверхеда. Ну или бинарное дерево, где узлы хранят сслыки на родителей. Еще в Rust из-за требований к безопасности памяти необходимо проверять гранциы массивов в рантайме при обращении по индексу. В системном языке все это напрягает.
Я готов пожертвовать 100%-й безопасностью памяти в обмен на отсутствие лишней возни и оверхеда. То есть в моем списке требований расту не хватает той самой бритвенной остроты. На том же C++ можно писать безопасный код довольно просто, соблюдая определенную дисциплину. Только хочется иметь «safe defaults». Например, убрать из языка оператор new (за исключением placement new), конструктор копирования по умолчанию и т.п.
— с ручным управлением памятью и детерминированными деструкторами;
— бритвенно острый, как C++;
— но с нормальным поведением по умолчанию, чтобы было меньше способов порезаться;
— с современной системой типов как в Rust.
Чувствую, не дождусь. Придется самому сделать, когда выйду на пенсию.
Конкретно в вашем примере может иметь смысл вынести логику преобразования элемента в отдельную функцию. А для преобразования массивов в Java 8 есть стандартные средства.
вместо
Да, с билдером не надо писать «object» каждый раз, но сложность кода остается точно такой же. Просто неважная синтаксическая делать.
(Совсем другое дело — использование неизменяемых (immutable) классов. Здесь билдер часто просто необходим, если в классе больше 3-5 полей)
Я как раз про них и говорю: может быть, можно обойтись без наследования исходных объектов?
Можно написать вспомогальный метод:
Но, по-моему, корень вашей проблемы в использовании наследования (которое, из моего скромного опыта, является верным решением менее чем в 1% случаев). Может быть, в вашем случае можно применить композицию вместо наследования?
Касательно приведенного кода: по-моему, вместо хранения ссылки на returnBuilder можно просто использовать (RetBuilder) this, если добавить ораничение RetBuilder extends BuilderImpl и подавить предупреждение об unchecked cast.
$ echo '2^1.5' | bc -l
Runtime warning (func=(main), adr=9): non-zero scale in exponent
2
Приходится извращаться с помощью экспоненты и логарифма, что не очень удобно:
$ echo 'e(l(2)*1.5)' | bc -l
2.82842712474619009755
Или какой-нибудь арккосинус если нужно посчитать, приходится использовать арктангенс.