JavaRush /Java блог /Random /Кофе-брейк #64. Как писать чистый код. Почему Java лучше,...

Кофе-брейк #64. Как писать чистый код. Почему Java лучше, чем C++ для систем с низким значением задержки

Статья из группы Random

Как писать чистый код

Источник: Dev.to Написание чистого кода похоже на написание стихов. Это поэзия, которая должна быть лаконичной, понятной и доступной для изменения. Чистый код предполагает масштабируемую организацию. Это означает, что внесение изменений не приводит к хаосу. Умение писать такой код — одно из ключевых качеств опытного разработчика. После того, как несколько людей посоветовали мне прочесть книгу «Чистый код», я, наконец, набралась храбрости и взялась за чтение. Оказалось, что это одна из тех книг, где обложка полностью оправдывает ажиотаж вокруг нее. Рекомендации в книге четкие, конкретные, практичные, да еще и поданы с юмором. Сегодня я хочу поделиться с вами основными выводами из этой книги.Кофе-брейк #64. Как писать чистый код. Почему Java лучше, чем C++, для систем с низким значением задержки - 1

1. Код должен быть не только рабочим, но и читаемым

Большую часть стоимости программного обеспечения завязана на долгосрочной поддержке. Поэтому код, который вы пишете, должен четко выражать ваши намерения. Он должен быть таким, чтобы новые разработчики, присоединившиеся к команде, могли легко уловить, что именно происходит в коде и почему. Чем более понятный код напишет автор, тем меньше времени потребуется другим разработчикам, чтобы в нем разобраться. Это снижает количество дефектов и стоимость обслуживания. Как этого достичь? Хороший нейминг + классы и функции с единой ответственностью + написание тестов.

2. Позже — значит никогда

Будем честны: все мы порой обещаем себе, что вернемся и почистим код позже, но в итоге забываем об этом. Не оставляйте куски бесполезного кода, которые уже не нужны. Они сбивают с толку других разработчиков и не имеют никакой ценности. Поэтому, внося изменения в функционал, всегда удаляйте старый код. Если при этом что-то где-то сломается, тесты все равно это сразу покажут. Как этого достичь? Удалять код бывает страшно, особенно в крупной архитектуре. Поэтому здесь ключевое значение имеют тесты. Они позволяют удалять код уверенно.

3. Функции должны быть небольшими

Первое правило написания функций — они должны быть маленькими, примерно до 20 строк. Чем меньше функция и чем более она сфокусирована на какой-то одной задаче, тем легче подобрать для нее хорошее имя. Что касается аргументов функции, их идеальное количество — 0. Дальше идет 1, 2, но нужно стараться, чтобы аргументов было не больше 3. Как этого достичь? Функции нужно писать в соответствии с принципами единой ответственности и открытости/закрытости.

4. Дублирование кода — это плохо

Дублирование — враг хорошо организованной системы. Это дополнительная работа, дополнительный риск и дополнительная ненужная сложность. Что с этим делать? Следите за тем, чтобы код писался в соответствии с принципом DRY, был изолированным и модульным.

5. Единственный хороший комментарий — это тот, который вы нашли способ не писать

«Нет ничего полезнее хорошего комментария в нужном месте. Но комментарии даже в наилучшем случае — неизбежное зло». Комментарии призваны компенсировать нашу неспособность выразить свою мысль в коде. То есть, это изначально признание поражения. Да, нам приходится их использовать, потому что мы не всегда можем явно донести свои намерения при помощи кода, но это не повод для радости. Дело в том, что комментарии часто лгут. Не всегда и не специально, но слишком часто. Чем старше комментарий и чем дальше он расположен от кода, который описывает, тем вероятнее, что он ошибочный. Причина этого проста: программисты не могут по-настоящему хорошо поддерживать и код, и все комментарии. Поэтому очень часто комментарии отделяются от кода, к которому относятся, и становятся бесхозными аннотациями с минимальной точностью. Что с этим делать? Нужно использовать методы описательного наименования. Прочитав имя переменной, вы должны сразу понять, что она из себя представляет. Также нужны тесты, чтобы другие разработчики понимали, какой функционал является главным.

6. Объект раскрывает поведение, но не данные

Модуль не должен знать о внутренностях объектов, которыми он манипулирует. Объекты скрывают свои данные и раскрывают операции. Это означает, что объект не должен раскрывать свою внутреннюю структуру через методы доступа. Не обязательно, чтобы все видели тебя голым. Что с этим делать? Область видимости переменных должна быть как можно более локальной, чтобы не раскрывать больше необходимого.

7. Тестирование

Код тестов так же важен, как и тот, что идет в производство. Поэтому он должен меняться и расти по мере развития проекта. Благодаря тестам ваш код остается гибким, поддерживаемым и пригодным для многократного использования. Без них любое изменение может повлечь за собой появление багов. Тесты позволяют чистить код, не боясь, что что-то поломается. Поэтому поддержание чистоты тестов имеет большое значение. Чистоту тестов обеспечивает их читаемость. Тесты — возможность объяснить другим разработчикам на простом языке намерения автора кода. Поэтому мы в каждой тестовой функции тестируем только одну концепцию. Так тест получается описательным, он легче читается, а если проваливается — легче отследить причину этого. Как этого достичь? Нужно следовать принципам чистых тестов FIRST. Тесты должны быть:
  • Быстрыми (Fast). Тесты должны выполняться быстро. Если вам приходится слишком долго ждать выполнения теста, вы вряд ли станете запускать его почаще.
  • Независимыми / изолированными (Independent). Тесты должны быть как можно более изолированными и не зависящими друг от друга.
  • Повторяемыми (Repeatable). Тесты должны быть повторяемыми в любой среде — в разработке, стейджинге и продакшене.
  • Очевидными (Self-Validating). Результатом выполнения теста должно быть булево значение. Тест должен быть или пройден, или провален.
  • Исчерпывающими (Thorough). Нужно стремиться охватить тестами все крайние случаи, все проблемы безопасности, каждый use case (вариант использования) и happy path (самый благоприятный сценарий работы кода).

8. Обработка ошибок и исключений

Каждое выбрасываемое вами исключение должно обеспечивать достаточный контекст, чтобы определить источник и местонахождение ошибки. Обычно у вас есть stack trace из любого исключения, но stack trace не расскажет вам о назначении операции, которая не удалась. По возможности избегайте передачи null в коде. Если у вас возникнет соблазн вернуть null из метода, подумайте лучше над возможностью создания исключения. Сделайте обработку ошибок отдельной задачей, которую можно просматривать независимо от основной логики. Как этого достичь? Создавайте информативные сообщения об ошибках и передавайте их вместе с вашими исключениями. Укажите неудачную операцию и тип ошибки.

9. Классы

Классы должны быть небольшими. Но считать надо не строки кода, а ответственность. Имена классов — ключ к описанию того, за что они отвечают. Наши системы должны состоять из большого количества маленьких классов, а не из нескольких огромных. Каждый такой маленький класс должен инкапсулировать единственную ответственность. Для существования каждого класса должна быть только одна конкретная причина, а для достижения желаемого поведения системы каждый класс должен «сотрудничать» с несколькими другими классами. Редко бывает уважительная причина для создания общедоступной переменной. Ослабление инкапсуляции — это всегда крайняя мера. Кроме того, переменных экземпляра должно быть немного. Хороший дизайн программного обеспечения позволяет вносить изменения без больших вложений и переделок. Сужение диапазона переменных упрощает эту задачу. Как этого достичь? Разделение проблем — один из старейших и важнейших приемов проектирования. Классы должны быть открыты для расширений, но закрыты для модификации. В идеальной системе мы включаем новые функции путем расширения системы, а не путем внесения изменений в существующий код.

10. Форматирование

Каждая пустая строка — это визуальная подсказка, помогающая определить, что началась новая, отдельная концепция. Локальные переменные должны появляться вверху функции. Переменные экземпляра должны декларироваться вверху класса. Короткие строки лучше чем длинные. Обычно предел — 100-120 символов, длиннее уже не стоит делать. Как этого достичь? Большинство параметров можно передать линтеру в вашем CI или текстовом редакторе. Пользуйтесь этими инструментами, чтобы сделать свой код как можно более чистым.

Принципы разработки программ

Применяйте следующие приемы, и ваш код всегда будет чист: Именование переменных. Подбор подходящих имен (хороший нейминг) имеет решающее значение для обеспечения читабельности кода и, следовательно, его поддерживаемости. «Выбирать имя для переменной следует так же ответственно, как и для своего первенца». Выбор хороших имен часто вызывает трудности у разработчиков. Для этого требуется хороший навык описания предметов и разделяемый культурный бэкграунд. Чистый код — код, который читают и улучшают совершенно разные разработчики. Имя переменной, функции или класса должно отвечать на все основные вопросы: почему эта сущность существует, для чего и как она используется. Если имя требует комментария, значит, оно недостаточно раскрывает суть того, что описывает. Более длинные имена важнее более коротких, а любое доступное для поиска имя лучше, чем константа. Однобуквенные имена могут использоваться только как локальные переменные внутри коротких методов: длина имени должна соответствовать области видимости. Имена методов должны быть глаголами или глагольными фразами; имя класса не должно быть глаголом. Зависимости должны быть сведены к минимуму. Лучше полагаться на то, что вы контролируете, чем на то, что не можете контролировать. В противном случае эти вещи будут контролировать вас. Аккуратность. Каждый отдельный фрагмент кода должен быть в том месте, где читатель ожидает его найти. Навигация по кодовой базе должна быть интуитивной, а намерения разработчика — понятными. Очистка. Не оставляйте в кодовой базе бесполезный код (старый и уже не использующийся или созданный «на всякий случай»). Уменьшайте количество дубликатов и создавайте простые абстракции на ранних этапах. Стандартизация. При написании кода следует придерживаться стиля и практик, установленных для репозитория. Самодисциплина. По мере развития использованных технологий и появления новых у разработчиков часто возникает стремление что-то изменить и улучшить в уже существующем коде. Не поддавайтесь хайпу слишком быстро: изучайте новые стеки основательно и только с конкретной целью. Поддержка чистоты кодовой базы — это нечто большее, чем вежливость по отношению к настоящим и будущим коллегам. Она необходима для выживания программы в долгосрочной перспективе. Чем чище ваш код, тем счастливее разработчики, тем лучше продукт и тем дольше он просуществует.

Почему Java лучше, чем C++, для систем с низким значением задержки

Источник: StackOverflow Как разработчики, мы все знаем, что есть два способа что-то делать: вручную, медленно и с раздражением, или автоматически, сложно и быстро. Я мог бы использовать искусственный интеллект, чтобы он написал за меня эту статью. Это могло бы сэкономить мне кучу времени — ИИ способен генерировать тысячи статей в секунду, но мой редактор вряд ли обрадуется, узнав, что генерация первой статьи займет два года.Кофе-брейк #64. Как писать чистый код. Почему Java лучше, чем C++, для систем с низким значением задержки - 2Аналогичная ситуация возникает при разработке программных систем с низким значением задержки. Принято считать, что было бы безумием использовать что-либо, кроме C++, потому что все остальное имеет слишком большую задержку. Но я здесь, чтобы убедить вас в противоположном, противоречащем интуиции, почти еретическом понятии: когда дело доходит до достижения низкой задержки в программных системах, лучше использовать Java. В этой статье я хочу взять конкретный пример программного обеспечения, для которого ценится низкая задержка: торговые системы. Однако приведенные здесь аргументы могут применяться практически к любым обстоятельствам, в которых требуется или желательна низкая задержка. Просто это легче обсуждать применительно к той области разработки, в которой у меня есть опыт. И правда в том, что задержку сложно измерить. Все сводится к тому, что вы понимаете под определением «низкая задержка». Давайте сейчас в этом разберемся.

Приобретенная мудрость

Поскольку C++ гораздо ближе к железу, большинство разработчиков скажут вам, что кодирование на этом языке дает преимущество в скорости. В ситуациях с малой задержкой, таких как высокоскоростная торговля, когда миллисекунды могут иметь значение между жизнеспособной частью программного обеспечения и устаревшей тратой дискового пространства, C++ считается золотым стандартом. По крайней мере, когда-то так было. Но реальность такова, что в настоящее время многие крупные банки и брокеры используют системы, написанные на Java. И я имею в виду изначально написанное на Java, а не написанное на Java, а затем интерпретированное на C++ с целью уменьшения задержки. Эти системы становятся стандартными даже для инвестиционных банков первого уровня, несмотря на то, что они (предположительно) медленнее. Итак, что происходит? Да, C++ может иметь «низкую задержку», когда дело доходит до выполнения кода, но это определенно не низкая задержка, когда дело доходит до развертывания новых функций или даже поиска разработчиков, которые могут его написать.

(Реальные) различия между Java и C++

Вопрос времени разработки — это только начало, когда речь идет о различиях между Java и C++ в реальных системах. Чтобы понять истинную ценность каждого языка в этом контексте, давайте немного углубимся. Во-первых, важно помнить настоящую причину, по которой C++ в большинстве ситуаций быстрее Java: указатель C++ является адресом переменной в памяти. Это означает, что программное обеспечение может напрямую обращаться к отдельным переменным, и ему не нужно просматривать таблицы, требующие больших вычислительных ресурсов, для их поиска. Или, по крайней мере, может обращаться, если будет указано, где они находятся, потому что с C++ вам часто придется явно управлять временем жизни и владением объектами. В результате, если вы действительно не очень хороши в написании кода (навык, на освоение которого могут потребоваться десятилетия), C++ потребует часов (или недель) отладки. И, как вам скажет любой, кто пытался отладить Monte Carlo engine или инструментальное средство проверки PDE, попытка отладки доступа к памяти на фундаментальном уровне может занять очень много времени. Один только неисправный указатель может легко вывести из строя всю систему, поэтому выпуск новой версии, написанной на C++, может быть поистине ужасающим. Конечно, это еще не все. Люди, которым нравится программировать на C++, укажут на то, что сборщик мусора в Java страдает от нелинейных всплесков задержки . Это особенно актуально при работе с устаревшими системами, поэтому отправка обновлений кода Java, не нарушая работу клиентских систем, может сделать их настолько медленными, что их нельзя будет использовать. В ответ я хотел бы отметить, что за последнее десятилетие была проделана большая работа по сокращению задержки, создаваемой Java GC. LMAX Disruptor, например, представляет собой торговую платформу с низким значением задержки, написанную на Java, также построенную как фреймворк, который имеет «механическое взаимодействие» с оборудованием, на котором он работает, и который не требует блокировки. Проблемы могут быть дополнительно смягчены, если вы создаете систему, которая использует процесс непрерывной интеграции и доставки (CI / CD), поскольку CI / CD допускает автоматическое развертывание проверенных изменений кода. Это связано с тем, что CI / CD обеспечивает итеративный подход к уменьшению задержек сборки мусора, при котором Java может постепенно улучшаться и адаптироваться к конкретным аппаратным средам без ресурсоемкого процесса подготовки кода для различных спецификаций оборудования перед его отправкой. Поскольку поддержка Java в IDE намного шире, чем в C++, большинство сред (Eclipse, IntelliJ IDEA) позволяют выполнять рефакторинг Java. Это означает, что IDE могут оптимизировать код для работы с низким значением задержки, хотя эта возможность все еще ограничена при работе с C++. Даже если код Java не совсем соответствует C++ по скорости, большинству разработчиков все равно легче достичь приемлемой производительности в Java, чем в C++.

Что мы подразумеваем под словом «быстрее»?

Фактически, есть веская причина усомниться в том, что C++ действительно «более быстрый» или вообще имеет «меньшую задержку», чем Java. Я осознаю, что попадаю в довольно “мутные воды”, и что многие разработчики начнут сомневаться в моем здравом уме. Но выслушайте меня. Представим такую ситуацию: у вас есть два разработчика — один пишет на C++, а другой на Java, и вы просите их написать платформу для высокоскоростной торговли с нуля. В результате система, написанная на Java, будет выполнять торговые транзакции дольше, чем система на C++. Однако Java имеет гораздо меньше случаев неопределенного поведения, чем C++. Возьмем только один пример: индексация за пределами массива является ошибкой как в Java, так и в C++. Если вы случайно сделаете это на C++, у вас может возникнуть ошибка segfault или (чаще) вы просто получите какое-то случайное число. В Java при выходе за границы всегда возникает ошибка ArrayIndexOutOfBoundsException. Это означает, что отладка в Java значительно упрощается, потому что ошибки, как правило, сразу определяются, и местонахождение ошибки легче отследить. Вдобавок, по крайней мере, по моему опыту, Java лучше распознает, какие фрагменты кода не нужно запускать, а какие критичны для работы вашего программного обеспечения. Вы, конечно, можете потратить дни на настройку своего кода C++, чтобы он не содержал абсолютно никакого постороннего кода, но в реальном мире каждая часть программного обеспечения содержит некоторую раздутость, и Java лучше распознает его автоматически. Это означает, что в реальном мире Java часто быстрее, чем C++, даже по стандартным показателям задержки. И даже там, где это не так, разница в задержке между языками часто перекрывается другими факторами, которые не настолько велики, чтобы иметь значение даже в высокоскоростной торговле.

Преимущества Java для систем с низкой задержкой

Все эти факторы, на мой взгляд, создают довольно неопровержимый аргумент в пользу использования Java для написания высокоскоростных торговых платформ (и систем с низким значением задержки в ​​целом, подробнее об этом чуть позже). Однако, чтобы немного повлиять на энтузиастов C++, давайте рассмотрим ряд дополнительных причин для использования Java:
  • Во-первых, любая избыточная задержка, которую Java привносит в ваше программное обеспечение, вероятно, будет намного меньше, чем другие факторы, влияющие на задержку — например, проблемы с интернетом. Это означает, что любой (хорошо написанный) код Java может легко работать так же, как C++, в большинстве торговых ситуаций.

  • Более короткое время разработки Java также означает, что в реальном мире программное обеспечение, написанное на Java, может быть быстрее адаптировано к изменению оборудования (или даже к новым торговым стратегиям), чем C++.

  • Если углубиться в это, вы увидите, что даже оптимизация программного обеспечения Java может быть быстрее (если рассматривать ее в рамках всего программного обеспечения), чем аналогичная задача на C++.

Иными словами, вы вполне можете писать код на Java с целью уменьшения задержки. Вам просто нужно написать его как C++, имея в виду управление памятью на каждом этапе разработки. Преимущество не писать в C++ состоит в том, что отладка, гибкая разработка и адаптация к нескольким средам в Java выполняется легче и быстрее.

Выводы

Если вы не разрабатываете торговые системы с низким значением задержки, вам, вероятно, будет интересно, применимо ли что-либо из вышеперечисленного именно к вам. Ответ, кроме очень немногих исключений, положительный. Споры о том, как добиться низкой задержки, не новы и не уникальны для мира финансов. По этой причине из него можно извлечь ценные уроки для других ситуаций. В частности, приведенный выше аргумент о том, что Java «лучше», потому что она более гибкая, более отказоустойчивая и, в конечном счете, быстрее в разработке и обслуживании, может применяться во многих областях разработки программного обеспечения. Причины, по которым я (лично) предпочитаю писать системы с низким значением задержки на Java, те же, что и те, которые сделали язык таким успешным за последние 25 лет. На Java легко писать, компилировать, выполнять отладку и изучать. Это означает, что вы можете тратить меньше времени на написание кода и больше времени на его оптимизацию. На практике это приводит к более надежным и быстрым торговым системам. И это все, что имеет значение для высокоскоростной торговли.
Комментарии (3)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
12 марта 2021
Прямо тянет на рекламу С++
Алексей Уровень 29
4 марта 2021
вторая часть вообще ниочем. Кому интересно - почитайте источник, точнее комментарии. Там эту статью ненавидят все, даже разработчики jvm. Такое чувству, что она для того и писалась - собрать побольше комментариев.
warlinux Уровень 41
4 марта 2021
из пальца высосано не встречал hft систем и нормального брокериджа на жаве