Rust как таковой здесь не важен, просто это единственный пример, для которого у меня есть актуальное время полного запуска LSP-сервера.
Если ваш LSP-сервер делает чуть больше, чем простую подсветку, он тоже будет тратить на анализ при запуске какое-то время. Просто берите проекты больше и больше, и в какой-то момент он тоже будет думать десятки секунд.
Впрочем, некоторые LSP (по ощущениям) при запуске как раз почти ничего не делают, зато потом на каждое действие тратят заметное время. Это ещё сильнее "компенсирует" скорость запуска редактора/IDE, потому что происходит многократно.
Из личного опыта: VS Code открывает средних размеров проект на Rust порядка 3-5 секунд, затем секунд 40 "думает" rust-analyzer. Пока проект анализируется, VS Code продолжает загружать другие плагины, но всё равно rust-analyzer фактически основной, пока он не закончит анализ, я редактировать исходники не буду.
Если бы VS Code запускалась мгновенно (или если бы это был vim/emacs/whatever, запускающееся "меньше секунды"), rust-analyzer всё равно бы думал те же 40 секунд, потому что это время зависит только от самого rust-analyzer и размера проекта, а не от того, какой клиент сидит на другой стороне LSP.
Разница между 40.x секунд и 45 уже не столь существенна, как между 0.x и 5, не правда ли?
вас не переубедить и меня никто не сможет переубедить
...
те кто не понимает такую позицию с ними бесполезно спорить, нервы тратятся в споре, а всë равно каждый остаëтся при своëм мнение.
А зачем вообще спорить, если вы сами осознаёте, что обе стороны не откажутся от своего мнения? Конечно же это будет холивар, где каждый считает себя единственным д'Артаньяном, и от обеих сторон аргументы будут вида "это всеобщая правда только потому что я так считаю".
Кому-то нравится панель задач размещать сбоку, а кто-то наоборот рад, что значки можно разместить по центру, а не на вполоборота головы, в углу этого самого сумермегаширокого 150:2 экрана. Кого-то раздражает, что "обычное" меню правой кнопкой теперь урезано и надо кликать с Shift, а кто-то рад, что теперь там только основные пункты, а не десяток "добавок" от сторонних программ, которые нужны раз в год... К тому же, и в Win 11 программы тоже могут добавлять пункты в "короткое" меню, тот же Notepad++ ведь сумел.
Было бы интересно, если бы как раз относительные "проценты скругления" было бы одинаковыми. Тогда или наушниками можно было бы пользоваться как дыроколом, или у мониторов радиус скругления был бы как у апельсина...
- Мариванна, почему вы мне это подчеркнули как ошибку, ведь Есенин/Маяковский/Шолохов/Какойтотамский именно так писали! - Это авторский стиль. - У меня тоже авторский стиль! - У тебя - нет, ты не автор. Как станешь известным автором, тогда и сможешь так писать.
До сих пор хочется таких учителей орфографическим словарём огреть.
Я даже не знал, что в Java такое добавили, единственный Java-проект с которым я взаимодействую (как разработчик) до сих пор на Java 8...
Когда я писал комментарий, я имел в виду switch-case как он есть в C, умеющий только сравнивать одно значение за ветку (то есть не pattern matching а value matching). Но конечно же никто не запрещает оставлять имеющиеся ключевые слова при наращивании возможностей.
Вопросов к статье... несколько, но ограничусь двумя:
А точно ли Material Icon Theme упрощает работу и/или повышает производительность (как заявлено в шапке статьи), оно же всего лишь меняет иконки файлов/папок в Explorer view. Если да - тогда докинули бы и пару глобальных тем, потому что оформление самого кода точно влияет сильнее, чем список файлов. Мне, например, нравится Bluloco Light (вариант Italic), хотя Bluloco Dark тоже неплоха (но я предпочитаю светлые).
JavaScript Debugger, который вы рекомендуете - это Nightly версия (на скриншоте даже видно). В базовой поставке VS Code уже есть встроенная релизная версия, убедиться можно если в списке расширений в поле поиска набрать @builtin.
Из своих рекомендаций - Error Lens. Позволяет подсвечивать всю строку с ошибкой/предупреждением, показывать текст ошибки рядом с кодом, показывать счётчики ошибок на панели статуса. Существенно упрощает оценку кода взглядом, ведь волнистые подчёркивания сильно менее заметны, нежели полностью подсвеченные строки.
Вот у нас есть проект программы, решающей уравнения. Функция решения квадратного уравнения написана в соответствии с алгоритмом от штатного математика, он единственный "ответственный". В целом проект насколько наSOLIDоленный, что сам Мартин бы расплакался от счастья (если бы знал про него, конечно).
Прекрасным сентябрьским днём в проект наняли отдельного дискриминантооптимизатора, чтоб ускорить решение, но через два дня уволили (потому что не надо приходить пьяным и материть одного из верхних менеджеров, он обижается). Код решения за всё это время не был изменён, просто не успели.
Правильно ли я вас понимаю, что сначала код удовлетворял SRP (а значит и SOLID в целом), затем нет, а затем снова да, несмотря на отсутствие изменений?
смысл в таких принципах же как раз в том что они должны давать достаточно четкий критерий.
Пример с SRP на словах, считаем действия, выполняемые функцией. Два варианта:
Функция получает на вход коэффициенты a,b,c квадратного уравнения, и возвращает массив с уникальными вещественными решениями.
Функция получает на вход коэффициенты a,b,c квадратного уравнения, считает дискриминант, определяет количество уникальных вещественных корней, считает эти корни, и наконец возвращает эти корни в массиве.
В первом случае она как будто бы делает одно действие, а во втором - аж четыре (!), хотя алгоритм абсолютно один и тот же. Но в первом случае "на слух" SRP соблюдён, а во втором надо разбивать на 3 части (см solve_quadratic ). А действительно ли это нужно? Да, в функции выполняется более одного шага, и отдельные микрофункции действительно будет проще обмазать тестами, но зачем? Решение квадратных уравнений - элементарный алгоритм, который буквально проходят в школе, да ещё и потом решают десятки если не сотни этих уравнений. Тем не менее, если фанатично следовать SRP - да, нужно разбивать, потому что Дядюшка превыше здравого смысла.
естественно, если процесс формирования требований был каким-то другим, то и разбиение будет иное. в этом фишка SRP - он смотрит не на сам код, а на предметную область и организацию процессов.
Насколько мне известно, это как раз и называется "не объективный". Объективные метрики могут быть измерены по чётко известной процедуре. Например, вычислительная сложность алгоритмов - есть и теоретический подход со всеми этими прекрасными O(n log n), и можно наделать практических тестов. А как измерить единственность ответственности?
А если вернуться к предыдущей цитате, то ещё забавнее получается. В текущий момент времени мне не надо иметь решение уравнения отдельно от печати - то есть YAGNI и KISS говорят, что дробить не надо (потому что больше функций - это не нужно и сложнее), а SRP - что надо. В итоге счёт 2:1 не в пользу SRP, бууу, принцип-неудачник?
SRP (как и многие другие принципы разработки ПО) не объективен потому что отсутствует конкретный алгоритм, можно только спросить разных людей и получить разные мнения.
В моём раздробленном примере выше:
функции print_0_solutions, print_1_solution, print_2_solutions следуют SRP, но они малы до состояния замусоривания
get_number_of_solutions имеет более одной причины быть переписанной (замена аргумента с дискриминанта на коэффициенты уравнения, добавление поддержки уравнений 1,3,4 степени, добавление поддержки комплексных чисел), то есть она не следует SRP - как разбивать будете?
Когда дядька-токарь, 3D-принтер, или станок с ЧПУ делают детальку, они знают, что эта деталька уйдёт в полностью спроектированное изделие, где все детальки подогнаны друг к другу по размерам и характеристикам. А когда дядька-программист делает функцию - он не знает, через сколько лет, месяцев, или даже дней требования к этой функции изменятся.
Требования к физическим объектам крайне редко меняются после их выпуска, и обычно с учётом наличия на рынке предыдущих версий изделий. Например, если 01.01.2024 выйдет закон, обязывающий автомобили иметь фиолетовую фару сзади, которая будет светить когда в машине есть дети - эти требования почти наверняка вступят в силу далеко не сразу, а для уже выпущенных автомобилей будут применяться "херак-херак" решения (например, прилепить фонарь к заднему стеклу, или поверх багажника).
А вот в программировании не так: сегодня вам сторонний API возвращает в JSON
- и всё, ваш код надо переписывать прямо сейчас, потому что он уже не работает. Да, та сторонняя компания выпустила уведомление о смене формата, за 3 дня, только в виде статьи в справочном центре, куда ещё надо знать как добраться. Нет, конечно же ни вы, ни коллеги / менеджеры не ходите в этот справочный центр каждый день, потому что зачем, ваш имеющийся код ведь работает (уже нет).
А потом может понадобится получение решение уравнение не только там. ну и в других местах.
Именно поэтому я написал "Вроде логично разбить на 2 подфункции: решение и получение численных ответов, и печать".
От функций код не станет менее нечитабельным
Опять-таки, всё дело в количестве, самостоятельности и возможности переиспользования этих функций.
Вот, в моём раздробленном примере выше:
calculate_discriminant полезна только для решения квадратных уравнений, больше эта формула нигде не используется - значит, функцию можно явно встроить в место вызова.
get_number_of_solutions - тем более подходит только для квадратных уравнений, ведь она смотрит на тот самый дискриминант из предыдущего пункта. Её, конечно можно адаптировать чтоб она смотрела на коэффициенты, но это будет полное переписывание её самой, и тех, кто её вызывает, и всё равно для уравнений разной степени там будут отдельные блоки кода.
print_0_solutions, print_1_solution, print_2_solutions - вы точно уверены, что в этих функциях-однострочниках есть хоть какая-то польза? Если вы завтра добавите поддержку уравнений вплоть до 4-й степени - вы напишете 5 таких однострочников? А если до 1000-й степени (не в общем виде, но это уже чисто алгебраический вопрос)? В какой момент вы решите, что надо вместо этих микрофункций использовать цикл по решениям и библиотеку, склоняющую слово "корень"? Но ведь это новое решение будет слишком сложным для квадратных уравнений, поэтому прямо сейчас пусть будет как есть (KISS)?
Дробление на функции - это как дробление чека в магазине: купить упаковку акварели отдельно от бананов - это одно (особенно учитывая что далеко не во всех магазинах есть сразу оба товара), но будете ли вы пробивать разными чеками макароны и хлеб, или тем более макароны разной формы? А ведь при подсчёте месячных трат именно на макароны, анализировать только "макаронные" чеки будет проще, чем выискивать подходящие товары из "общепродуктовых" чеков...
Проблема не в том, чтоб кислород для дыхания стал сжат вдвое сильнее, этим всё равно занимается наземный агрегат, а для перекачивания внутри ракеты, уже для дыхания, особой мощности и так не надо. Проблема в том, что теперь баки с дыхательным кислородом станут вдвое тяжелее, а ещё проектной толщины стенок может просто не хватить и придётся баки либо делать с ещё более толстыми стенками, либо ставить два бака рядом ( = существенное изменение конструкции). А ещё эти мешки с мясом туристы не только суммарно вдохнут вдвое больше, но и выдохнут вдвое больше, углекислый газ надо тоже куда-то девать.
А уж увеличение кабины в два раза - вообще смешно, это почти как в доме пытаться заменить лифт на вдвое более человекоподъёмный: мотор и трос ладно, а с кабиной-то что делать, пусть люди напихиваются вдвое плотнее? Тут надо всю шахту переделывать, а это перестройка части / всего здания... о чём и статья.
Или ещё лучше: ваша фирма производит фонарики, и вам пришёл заказ на 150 штук со следующими требованиями: питание от 3-х батареек AA, 1 лампочка накаливания, кнопку необходимо удерживать (не защёлкивается). "Отлично", думаете вы, "типичный набор компонентов". Потом заказчик присылает письмо, в котором просит заменить лампочку на светодиод (в интернете вычитал, что они служат дольше и потребляют меньше энергии). Вы пожимаете плечами: "Ну ладно, конструкция почти та же самая, только вместо винтового патрона для лампочки - небольшая печатная плата со светодиодом." Через 2 дня заказчик просит вас заменить батарейки на AAA, якобы для уменьшения веса. "Хм, придётся вставлять другой отсек для батареек, а отверстия от уменьшенных габаритов компенсировать дополнительными пластиковыми перегородками". А затем заказчик просит вас поставить матрицу из 18 светодиодов, питание заменить на 3 батарейки типа D, а ещё добавить функции включения на половину и мигания красным. Как думаете, сколько компонентов у вас останется от предыдущей конструкции? Я думаю, что в лучшем случае кнопка
А как определить, сколько действий выполняет данный код, и самое главное - может ли количество действий в одном и том же коде различаться для разных задач/требований?
Например, вот эта функция печати решений квадратного уравнения - сколь действия она делает? На сколько более мелких функций её надо разбивать, и надо ли вообще разбивать?
function print_quadratic_solutions(a, b, c) {
const d = b * b - 4 * a * c;
if (d < 0) {
console.log("0 корней");
} else if (d == 0) {
console.log("1 корень", -b / (2 * a));
} else {
console.log("2 корня", -b - Math.sqrt(d) / (2 * a), -b + Math.sqrt(d) / (2 * a));
}
}
Вроде логично разбить на 2 подфункции: решение и получение численных ответов, и печать, верно? А если следовать заветам Дядюшки Боба очень фанатично, то можно и до
такого дойти
function print_0_solutions() {
console.log("0 корней");
}
function print_1_solution(x) {
console.log("1 корень", x);
}
function print_2_solutions(x1, x2) {
console.log("2 корня", x1, x2);
}
function print_solutions(x) {
switch (x.length) {
case 0: {
print_0_solutions();
break;
}
case 1: {
print_1_solution(x[0]);
break;
}
case 2: {
print_2_solutions(x[0], x[1]);
break
}
}
}
function get_number_of_solutions(d) {
if (d < 0) {
return 0;
} else if (d == 0) {
return 1;
} else {
return 2;
}
}
function calculate_discriminant(a, b, c) {
return b * b - 4 * a * c;
}
function solve_quadratic(a, b, c) {
const d = calculate_discriminant(a, b, c);
const n = get_number_of_solutions(d);
switch (n) {
case 0:
return [];
case 1:
return [-b / (2 * a)];
case 2:
return [-b - Math.sqrt(d) / (2 * a), -b + Math.sqrt(d) / (2 * a)];
}
}
function print_quadratic_solutions(a, b, c) {
const solutions = solve_quadratic(a, b, c);
print_solutions(solutions);
}
В 6 раз длиннее, зато каждая функция настолько мала, насколько возможно, ага.
А теперь представьте, что этот код размещён на HTML-странице, где есть поля ввода для a,b,c и кнопка для решения. Я бы вообще не стал делить этот код на функции, или даже отделять от получения значений из HTML-элементов, мне это кажется избыточным, я бы всё это поместил в единственный onсlick-обработчик.
С мобильным рынком вообще страшно получается. Многие проекты - бесплатны, но с микротранзациями. Активно платящих игроков на самом деле не так много, подавляющее большинство или не платит вообще, или очень редко и немного; практически вся прибыль идёт от ничтожной доли "китов". И беда в том, что эта незначительная доля активных плательщиков может легко "протащить" проект через границу пороговой прибыли, после которой внезапно окажется, что все эти миллионы pure Free2Play игроков не просто "отбирают" у компании деньги на поддержку инфраструктуры, но и теперь вынуждают переводить их в Unity.
В Unity пояснили, что у Unity Runtime на данный момент миллиарды установок, а компании необходимы дополнительные ресурсы, чтобы продолжить и дальше вкладывать в код и предоставлять сервисы для разработчиков на должном уровне.
Аргументный аргумент, однако. Сколько миллиардов установок у какого-нибудь OpenSSL? Представьте, что они будут взимать плату с владельцев сайтов / хостеров за каждое посещение (пусть даже и после достижения определённого порога, пусть даже и только с тех сайтов, которые приносят какую-либо денежную прибыль владельцам). А то и вовсе GNU начнёт взимать плату за каждую установку бинарей, собранных с помощью GCC, вот хорошо будет.
Ну и да, Unity Runtime использует .Net - но ой как сомневаюсь, что они захотят отчислять, скажем, половину от своего Unity Runtime Fee в Microsoft...
Один нативно не поддерживает все эти типы. Чтобы работать с ними, нужно подтянуть некоторое количество зависимостей (библиотек). ... Второй язык все это поддерживает нативно
Если второму языку не нужно даже библиотек для подключения к БД (то есть всё нужное есть либо в стандартной библиотеке самого языка, либо предоставлено специальным рантаймом) - значит он специально для взаимодействия с БД разрабатывался, так? А как у него обстоят дела с поддержкой всех других смежных областей, для которых требуется взаимодействие с БД? То есть, если, например, у меня есть вебсайт, работающий с БД - второй язык определённо удобнее с этой стороны, а что у него с поддержкой HTTP(S)? А другому пользователю нужно проводить аналитику по данным из БД - как дела со статистикой в этом втором БД-языке? А третьему пользователю... А четвёртому... То есть либо в итоге второй язык превращается в тот самый комбайн, от которых вы предлагаете избавляться, либо он хорош для одной-двух задач (например, конвертация данных из БД в один из текстовых форматов типа XML/JSON/YAML), а остальным всё равно придётся использовать нечто другое, вместо или вместе с этим БД-языком.
В одном языке вам для этого нужно создать объект string (опять конструктор вызывать) который содержит нужные методы.
Во втором ничего этого не надо - просто пишем цикл типа for-each word in split(phrase) и работаем непосредственно с той фразой, что получили из записи в БД. Без создания лишних сущностей (объектов).
То есть вы описываете примерно такую ситуацию, верно?
// Язык 1
var a = String("abcde");
a.split().forEach(process_segment);
// Язык 2.1
var a = "abcde";
forEach(split(a), process_segment);
// Язык 2.2
var a = "abcde";
split(a).forEach(process_segment);
Но ведь разница - исключительно синтаксическая, в момент создания. Более того, в вашем примере вы уже имеете сущность записи из БД, которая почти наверняка будет представлена как массив структур-строк с полями-колонками, кто же вам мешает обращаться к полям этих самых структур напрямую, без явного копирования в новые переменные?
Реализуете на JS? Будет работать?
Вы упустили (судя по вашему ответу и даже по цитате) два очень важных символа в моём предыдущем сообщении: /s. Это обозначение сарказма в текущем предложении (иногда в целом абзаце). Но я не удивлюсь, если на npmjs.com найдутся пакеты для взаимодействия со многими +- распространёнными железками. Например, raspi-sensors (устаревший) или i2c-bus - раньше с железками на ASM/C взаимодействовали, потом пришла Arduino со своим C++ с библиотеками, более известном как Wiring (он был и до Arduino, но не так известен), а теперь вот можно и из JS битиками в провода пулять.
"Универсальный ЯП" - это что-то типа машины, которая и для Формулы-1 и для Париж-Даккар. Не находите, что несколько странная концепция?
Только вот в информатике, в отличие от, пожалуй, любой другой области, как раз есть такая универсальная машина, спасибо Тьюрингу.
То, насколько активно вы предлагаете дробить языки по их области применения, выглядит слишком уж радикально. Да, объединять всё в единый СуперЯзык+++ - не очень разумно, но и 100500 очень эффективных, но очень узкоспециализированных языков - это тоже перебор. Нелегко определить границу, когда "много" становится "слишком много", когда чайная ложка №317 - для клубничного мороженого, а ложка №318 - для шоколадного (у них разный состав сплава, что придаёт им чуть разный цвет, поэтому на фотографиях они смотрятся лучше именно так, а не наоборот). Нишевые языки конечно же могут существовать, но для их существования должно быть достаточно причин: востребованность, универсальность в рамках своей ниши (представьте, что, например, jq не умеет работать с JSONами, у которых корень - не объект), наличие ресурсов на поддержку и разработку (либо "замороженная" предметная область, что позволит сделать поистине готовый продукт).
И даже если будет множество ну очень уж хороших нишевых языков, всё равно будут востребованы пусть и менее удобные, но более универсальные. Примерно как если вы пишите программу для работы с медиафайлами, то для поддержки всего одного-двух форматов лучше подключить конкретные библиотеки (например, libflac + liblame), но если вы хотите более универсальный продукт - вы обподключаетесь по одной, намного проще сразу libav взять.
Хотя, универсальный язык существует, JavaScript называется. Скоро на нём перепишут вообще всё /s
освоить еще один абсолютно несложно
Если только вы при этом не прыгаете с одной парадигмы на другую. После многих лет отборного ООП, переход на (первый) функциональный язык будет... не столь быстрым.
Подскажите, почему в уравнениях Максвелла в одном случае операция умножения обозначается точкой, а в других "х"?
Точкой обозначается скалярное произведение (длины векторов умноженные на косинус угла между ними), а крестиком - векторное (результат - вектор, длина = длины векторов умноженные на синус угла между ними, направление - перпендикулярно обоим исходным, пальцы правой руки в раскоряку).
Rust как таковой здесь не важен, просто это единственный пример, для которого у меня есть актуальное время полного запуска LSP-сервера.
Если ваш LSP-сервер делает чуть больше, чем простую подсветку, он тоже будет тратить на анализ при запуске какое-то время. Просто берите проекты больше и больше, и в какой-то момент он тоже будет думать десятки секунд.
Впрочем, некоторые LSP (по ощущениям) при запуске как раз почти ничего не делают, зато потом на каждое действие тратят заметное время. Это ещё сильнее "компенсирует" скорость запуска редактора/IDE, потому что происходит многократно.
Из личного опыта: VS Code открывает средних размеров проект на Rust порядка 3-5 секунд, затем секунд 40 "думает" rust-analyzer. Пока проект анализируется, VS Code продолжает загружать другие плагины, но всё равно rust-analyzer фактически основной, пока он не закончит анализ, я редактировать исходники не буду.
Если бы VS Code запускалась мгновенно (или если бы это был vim/emacs/whatever, запускающееся "меньше секунды"), rust-analyzer всё равно бы думал те же 40 секунд, потому что это время зависит только от самого rust-analyzer и размера проекта, а не от того, какой клиент сидит на другой стороне LSP.
Разница между 40.x секунд и 45 уже не столь существенна, как между 0.x и 5, не правда ли?
А зачем вообще спорить, если вы сами осознаёте, что обе стороны не откажутся от своего мнения? Конечно же это будет холивар, где каждый считает себя единственным д'Артаньяном, и от обеих сторон аргументы будут вида "это всеобщая правда только потому что я так считаю".
Кому-то нравится панель задач размещать сбоку, а кто-то наоборот рад, что значки можно разместить по центру, а не на вполоборота головы, в углу этого самого сумермегаширокого 150:2 экрана.
Кого-то раздражает, что "обычное" меню правой кнопкой теперь урезано и надо кликать с Shift, а кто-то рад, что теперь там только основные пункты, а не десяток "добавок" от сторонних программ, которые нужны раз в год... К тому же, и в Win 11 программы тоже могут добавлять пункты в "короткое" меню, тот же Notepad++ ведь сумел.
Она именно "эргономичная", с жирными кавычками.
Было бы интересно, если бы как раз относительные "проценты скругления" было бы одинаковыми. Тогда или наушниками можно было бы пользоваться как дыроколом, или у мониторов радиус скругления был бы как у апельсина...
- Мариванна, почему вы мне это подчеркнули как ошибку, ведь Есенин/Маяковский/Шолохов/Какойтотамский именно так писали!
- Это авторский стиль.
- У меня тоже авторский стиль!
- У тебя - нет, ты не автор. Как станешь известным автором, тогда и сможешь так писать.
До сих пор хочется таких учителей орфографическим словарём огреть.
Я даже не знал, что в Java такое добавили, единственный Java-проект с которым я взаимодействую (как разработчик) до сих пор на Java 8...
Когда я писал комментарий, я имел в виду
switch-caseкак он есть в C, умеющий только сравнивать одно значение за ветку (то есть не pattern matching а value matching). Но конечно же никто не запрещает оставлять имеющиеся ключевые слова при наращивании возможностей.Вопросов к статье... несколько, но ограничусь двумя:
А точно ли Material Icon Theme упрощает работу и/или повышает производительность (как заявлено в шапке статьи), оно же всего лишь меняет иконки файлов/папок в Explorer view. Если да - тогда докинули бы и пару глобальных тем, потому что оформление самого кода точно влияет сильнее, чем список файлов. Мне, например, нравится Bluloco Light (вариант Italic), хотя Bluloco Dark тоже неплоха (но я предпочитаю светлые).
JavaScript Debugger, который вы рекомендуете - это Nightly версия (на скриншоте даже видно). В базовой поставке VS Code уже есть встроенная релизная версия, убедиться можно если в списке расширений в поле поиска набрать
@builtin.Из своих рекомендаций - Error Lens. Позволяет подсвечивать всю строку с ошибкой/предупреждением, показывать текст ошибки рядом с кодом, показывать счётчики ошибок на панели статуса. Существенно упрощает оценку кода взглядом, ведь волнистые подчёркивания сильно менее заметны, нежели полностью подсвеченные строки.
Эээ...
Вот у нас есть проект программы, решающей уравнения. Функция решения квадратного уравнения написана в соответствии с алгоритмом от штатного математика, он единственный "ответственный". В целом проект насколько наSOLIDоленный, что сам Мартин бы расплакался от счастья (если бы знал про него, конечно).
Прекрасным сентябрьским днём в проект наняли отдельного дискриминантооптимизатора, чтоб ускорить решение, но через два дня уволили (потому что не надо приходить пьяным и материть одного из верхних менеджеров, он обижается). Код решения за всё это время не был изменён, просто не успели.
Правильно ли я вас понимаю, что сначала код удовлетворял SRP (а значит и SOLID в целом), затем нет, а затем снова да, несмотря на отсутствие изменений?
Пример с SRP на словах, считаем действия, выполняемые функцией. Два варианта:
Функция получает на вход коэффициенты a,b,c квадратного уравнения, и возвращает массив с уникальными вещественными решениями.
Функция получает на вход коэффициенты a,b,c квадратного уравнения, считает дискриминант, определяет количество уникальных вещественных корней, считает эти корни, и наконец возвращает эти корни в массиве.
В первом случае она как будто бы делает одно действие, а во втором - аж четыре (!), хотя алгоритм абсолютно один и тот же. Но в первом случае "на слух" SRP соблюдён, а во втором надо разбивать на 3 части (см
solve_quadratic). А действительно ли это нужно? Да, в функции выполняется более одного шага, и отдельные микрофункции действительно будет проще обмазать тестами, но зачем? Решение квадратных уравнений - элементарный алгоритм, который буквально проходят в школе, да ещё и потом решают десятки если не сотни этих уравнений. Тем не менее, если фанатично следовать SRP - да, нужно разбивать, потому что Дядюшка превыше здравого смысла.Насколько мне известно, это как раз и называется "не объективный". Объективные метрики могут быть измерены по чётко известной процедуре. Например, вычислительная сложность алгоритмов - есть и теоретический подход со всеми этими прекрасными O(n log n), и можно наделать практических тестов. А как измерить единственность ответственности?
А если вернуться к предыдущей цитате, то ещё забавнее получается. В текущий момент времени мне не надо иметь решение уравнения отдельно от печати - то есть YAGNI и KISS говорят, что дробить не надо (потому что больше функций - это не нужно и сложнее), а SRP - что надо. В итоге счёт 2:1 не в пользу SRP, бууу, принцип-неудачник?
SRP (как и многие другие принципы разработки ПО) не объективен потому что отсутствует конкретный алгоритм, можно только спросить разных людей и получить разные мнения.
В моём раздробленном примере выше:
функции
print_0_solutions, print_1_solution, print_2_solutionsследуют SRP, но они малы до состояния замусориванияget_number_of_solutionsимеет более одной причины быть переписанной (замена аргумента с дискриминанта на коэффициенты уравнения, добавление поддержки уравнений 1,3,4 степени, добавление поддержки комплексных чисел), то есть она не следует SRP - как разбивать будете?Когда дядька-токарь, 3D-принтер, или станок с ЧПУ делают детальку, они знают, что эта деталька уйдёт в полностью спроектированное изделие, где все детальки подогнаны друг к другу по размерам и характеристикам. А когда дядька-программист делает функцию - он не знает, через сколько лет, месяцев, или даже дней требования к этой функции изменятся.
Требования к физическим объектам крайне редко меняются после их выпуска, и обычно с учётом наличия на рынке предыдущих версий изделий. Например, если 01.01.2024 выйдет закон, обязывающий автомобили иметь фиолетовую фару сзади, которая будет светить когда в машине есть дети - эти требования почти наверняка вступят в силу далеко не сразу, а для уже выпущенных автомобилей будут применяться "херак-херак" решения (например, прилепить фонарь к заднему стеклу, или поверх багажника).
А вот в программировании не так: сегодня вам сторонний API возвращает в JSON
массив объектов
а завтра этот же самый массив
положат в объект-обёртку
- и всё, ваш код надо переписывать прямо сейчас, потому что он уже не работает. Да, та сторонняя компания выпустила уведомление о смене формата, за 3 дня, только в виде статьи в справочном центре, куда ещё надо знать как добраться. Нет, конечно же ни вы, ни коллеги / менеджеры не ходите в этот справочный центр каждый день, потому что зачем, ваш имеющийся код ведь работает (уже нет).
Именно поэтому я написал "Вроде логично разбить на 2 подфункции: решение и получение численных ответов, и печать".
Опять-таки, всё дело в количестве, самостоятельности и возможности переиспользования этих функций.
Вот, в моём раздробленном примере выше:
calculate_discriminantполезна только для решения квадратных уравнений, больше эта формула нигде не используется - значит, функцию можно явно встроить в место вызова.get_number_of_solutions- тем более подходит только для квадратных уравнений, ведь она смотрит на тот самый дискриминант из предыдущего пункта. Её, конечно можно адаптировать чтоб она смотрела на коэффициенты, но это будет полное переписывание её самой, и тех, кто её вызывает, и всё равно для уравнений разной степени там будут отдельные блоки кода.print_0_solutions, print_1_solution, print_2_solutions- вы точно уверены, что в этих функциях-однострочниках есть хоть какая-то польза? Если вы завтра добавите поддержку уравнений вплоть до 4-й степени - вы напишете 5 таких однострочников? А если до 1000-й степени (не в общем виде, но это уже чисто алгебраический вопрос)? В какой момент вы решите, что надо вместо этих микрофункций использовать цикл по решениям и библиотеку, склоняющую слово "корень"? Но ведь это новое решение будет слишком сложным для квадратных уравнений, поэтому прямо сейчас пусть будет как есть (KISS)?Дробление на функции - это как дробление чека в магазине: купить упаковку акварели отдельно от бананов - это одно (особенно учитывая что далеко не во всех магазинах есть сразу оба товара), но будете ли вы пробивать разными чеками макароны и хлеб, или тем более макароны разной формы? А ведь при подсчёте месячных трат именно на макароны, анализировать только "макаронные" чеки будет проще, чем выискивать подходящие товары из "общепродуктовых" чеков...
Проблема не в том, чтоб кислород для дыхания стал сжат вдвое сильнее, этим всё равно занимается наземный агрегат, а для перекачивания внутри ракеты, уже для дыхания, особой мощности и так не надо. Проблема в том, что теперь баки с дыхательным кислородом станут вдвое тяжелее, а ещё проектной толщины стенок может просто не хватить и придётся баки либо делать с ещё более толстыми стенками, либо ставить два бака рядом ( = существенное изменение конструкции). А ещё э
ти мешки с мясомтуристы не только суммарно вдохнут вдвое больше, но и выдохнут вдвое больше, углекислый газ надо тоже куда-то девать.А уж увеличение кабины в два раза - вообще смешно, это почти как в доме пытаться заменить лифт на вдвое более человекоподъёмный: мотор и трос ладно, а с кабиной-то что делать, пусть люди напихиваются вдвое плотнее? Тут надо всю шахту переделывать, а это перестройка части / всего здания... о чём и статья.
Или ещё лучше: ваша фирма производит фонарики, и вам пришёл заказ на 150 штук со следующими требованиями: питание от 3-х батареек AA, 1 лампочка накаливания, кнопку необходимо удерживать (не защёлкивается). "Отлично", думаете вы, "типичный набор компонентов".
Потом заказчик присылает письмо, в котором просит заменить лампочку на светодиод (в интернете вычитал, что они служат дольше и потребляют меньше энергии). Вы пожимаете плечами: "Ну ладно, конструкция почти та же самая, только вместо винтового патрона для лампочки - небольшая печатная плата со светодиодом."
Через 2 дня заказчик просит вас заменить батарейки на AAA, якобы для уменьшения веса. "Хм, придётся вставлять другой отсек для батареек, а отверстия от уменьшенных габаритов компенсировать дополнительными пластиковыми перегородками".
А затем заказчик просит вас поставить матрицу из 18 светодиодов, питание заменить на 3 батарейки типа D, а ещё добавить функции включения на половину и мигания красным. Как думаете, сколько компонентов у вас останется от предыдущей конструкции? Я думаю, что в лучшем случае кнопка
А как определить, сколько действий выполняет данный код, и самое главное - может ли количество действий в одном и том же коде различаться для разных задач/требований?
Например, вот эта функция печати решений квадратного уравнения - сколь действия она делает? На сколько более мелких функций её надо разбивать, и надо ли вообще разбивать?
Вроде логично разбить на 2 подфункции: решение и получение численных ответов, и печать, верно? А если следовать заветам Дядюшки Боба очень фанатично, то можно и до
такого дойти
В 6 раз длиннее, зато каждая функция настолько мала, насколько возможно, ага.
А теперь представьте, что этот код размещён на HTML-странице, где есть поля ввода для a,b,c и кнопка для решения. Я бы вообще не стал делить этот код на функции, или даже отделять от получения значений из HTML-элементов, мне это кажется избыточным, я бы всё это поместил в единственный onсlick-обработчик.
И ведь никто не скажет как объективно лучше...
С мобильным рынком вообще страшно получается. Многие проекты - бесплатны, но с микротранзациями. Активно платящих игроков на самом деле не так много, подавляющее большинство или не платит вообще, или очень редко и немного; практически вся прибыль идёт от ничтожной доли "китов". И беда в том, что эта незначительная доля активных плательщиков может легко "протащить" проект через границу пороговой прибыли, после которой внезапно окажется, что все эти миллионы pure Free2Play игроков не просто "отбирают" у компании деньги на поддержку инфраструктуры, но и теперь вынуждают переводить их в Unity.
Аргументный аргумент, однако. Сколько миллиардов установок у какого-нибудь OpenSSL? Представьте, что они будут взимать плату с владельцев сайтов / хостеров за каждое посещение (пусть даже и после достижения определённого порога, пусть даже и только с тех сайтов, которые приносят какую-либо денежную прибыль владельцам). А то и вовсе GNU начнёт взимать плату за каждую установку бинарей, собранных с помощью GCC, вот хорошо будет.
Ну и да, Unity Runtime использует .Net - но ой как сомневаюсь, что они захотят отчислять, скажем, половину от своего Unity Runtime Fee в Microsoft...
Если второму языку не нужно даже библиотек для подключения к БД (то есть всё нужное есть либо в стандартной библиотеке самого языка, либо предоставлено специальным рантаймом) - значит он специально для взаимодействия с БД разрабатывался, так? А как у него обстоят дела с поддержкой всех других смежных областей, для которых требуется взаимодействие с БД? То есть, если, например, у меня есть вебсайт, работающий с БД - второй язык определённо удобнее с этой стороны, а что у него с поддержкой HTTP(S)? А другому пользователю нужно проводить аналитику по данным из БД - как дела со статистикой в этом втором БД-языке? А третьему пользователю... А четвёртому... То есть либо в итоге второй язык превращается в тот самый комбайн, от которых вы предлагаете избавляться, либо он хорош для одной-двух задач (например, конвертация данных из БД в один из текстовых форматов типа XML/JSON/YAML), а остальным всё равно придётся использовать нечто другое, вместо или вместе с этим БД-языком.
То есть вы описываете примерно такую ситуацию, верно?
Но ведь разница - исключительно синтаксическая, в момент создания. Более того, в вашем примере вы уже имеете сущность записи из БД, которая почти наверняка будет представлена как массив структур-строк с полями-колонками, кто же вам мешает обращаться к полям этих самых структур напрямую, без явного копирования в новые переменные?
Вы упустили (судя по вашему ответу и даже по цитате) два очень важных символа в моём предыдущем сообщении:
/s. Это обозначение сарказма в текущем предложении (иногда в целом абзаце).Но я не удивлюсь, если на npmjs.com найдутся пакеты для взаимодействия со многими +- распространёнными железками. Например,
raspi-sensors(устаревший) или i2c-bus - раньше с железками на ASM/C взаимодействовали, потом пришла Arduino со своим C++ с библиотеками, более известном как Wiring (он был и до Arduino, но не так известен), а теперь вот можно и из JS битиками в провода пулять.Только вот в информатике, в отличие от, пожалуй, любой другой области, как раз есть такая универсальная машина, спасибо Тьюрингу.
То, насколько активно вы предлагаете дробить языки по их области применения, выглядит слишком уж радикально. Да, объединять всё в единый СуперЯзык+++ - не очень разумно, но и 100500 очень эффективных, но очень узкоспециализированных языков - это тоже перебор. Нелегко определить границу, когда "много" становится "слишком много", когда чайная ложка №317 - для клубничного мороженого, а ложка №318 - для шоколадного (у них разный состав сплава, что придаёт им чуть разный цвет, поэтому на фотографиях они смотрятся лучше именно так, а не наоборот). Нишевые языки конечно же могут существовать, но для их существования должно быть достаточно причин: востребованность, универсальность в рамках своей ниши (представьте, что, например, jq не умеет работать с JSONами, у которых корень - не объект), наличие ресурсов на поддержку и разработку (либо "замороженная" предметная область, что позволит сделать поистине готовый продукт).
И даже если будет множество ну очень уж хороших нишевых языков, всё равно будут востребованы пусть и менее удобные, но более универсальные. Примерно как если вы пишите программу для работы с медиафайлами, то для поддержки всего одного-двух форматов лучше подключить конкретные библиотеки (например, libflac + liblame), но если вы хотите более универсальный продукт - вы обподключаетесь по одной, намного проще сразу libav взять.
Хотя, универсальный язык существует, JavaScript называется. Скоро на нём перепишут вообще всё /s
Если только вы при этом не прыгаете с одной парадигмы на другую. После многих лет отборного ООП, переход на (первый) функциональный язык будет... не столь быстрым.
Точкой обозначается скалярное произведение (длины векторов умноженные на косинус угла между ними), а крестиком - векторное (результат - вектор, длина = длины векторов умноженные на синус угла между ними, направление - перпендикулярно обоим исходным, пальцы правой руки в раскоряку).