Хочешь уверенно проходить IT-интервью?
Мы понимаем, как сложно подготовиться: стресс, алгоритмы, вопросы, от которых голова идёт кругом. Но с AI тренажёром всё гораздо проще.
💡 Почему Т1 тренажёр — это мастхэв?
- Получишь настоящую обратную связь: где затык, что подтянуть и как стать лучше
- Научишься не только решать задачи, но и объяснять своё решение так, чтобы интервьюер сказал: "Вау!".
- Освоишь все этапы собеседования, от вопросов по алгоритмам до диалога о твоих целях.
Зачем листать миллион туториалов? Просто зайди в Т1 тренажёр, потренируйся и уверенно удиви интервьюеров. Мы не обещаем лёгкой прогулки, но обещаем, что будешь готов!
Реклама. ООО «Смарт Гико», ИНН 7743264341. Erid 2VtzqwP8vqy
Про Big O нотацию вы наверняка сами знаете, а мы вам расскажем про Bug O нотацию и отладочную сложность программных интерфейсов.
Хотите писать эффективный и высокопроизводительный код? Тогда вы непременно должны учитывать его алгоритмическую сложность – то, что также известно под кодовым именем Big O нотация.
Big O описывает, насколько медленнее станет выполняться ваш код, если увеличить размер входных данных. Для примера возьмем сортировку. Сложность O(n2) означает, что если входной массив увеличится в 50 раз, то время выполнения алгоритма вырастет аж в 502 = 2500 раз!
Big O нотация – это не точное число, она дает лишь общее представление о масштабируемости алгоритма, например, O(n), O(n log n), O(n2), O(n!).
Читайте также:
- Анализ алгоритмов для начинающих: вводное руководство
- Какая сортировка самая быстрая? Тестируем алгоритмы
- Вводный курс по алгоритмам: от сортировок до машины Тьюринга
На этом разговор про алгоритмы и производительность мы заканчиваем. Цель статьи – обсуждение программных интерфейсов и отладки программ. Вроде бы темы не связанные друг с другом, однако вы удивитесь, как много между ними общего.
Огромная доля нашего рабочего времени уходит на поиск и исправление ошибок. И чем больше кода – тем больше времени.
Некоторые API и языковые конструкции устроены так, что делают невозможным допущение целых классов ошибок, а другие, наоборот, создают бесконечные проблемы на пустом месте. Как же понять, кто из них кто?
Мы часто спорим об изящности наших программных интерфейсов, но иногда забываем, что изящность и опыт практического использования – это разные вещи. Чтобы оценить, насколько замедляет вас ваш API по мере роста кодовой базы, введем новую нотацию: Bug O.
Отладочная сложность: ?(n)
Big O описывает, как замедляется алгоритм, Bug O – как замедляетесь вы.
Посмотрим на реальном примере. Есть довольно неструктурированный код, вручную обновляющий DOM с помощью императивных операций вроде node.appendChild()
и node.removeChild()
.
function trySubmit() { // Section 1 let spinner = createSpinner(); formStatus.appendChild(spinner); submitForm().then(() => { // Section 2 formStatus.removeChild(spinner); let successMessage = createSuccessMessage(); formStatus.appendChild(successMessage); }).catch(error => { // Section 3 formStatus.removeChild(spinner); let errorMessage = createErrorMessage(error); let retryButton = createRetryButton(); formStatus.appendChild(errorMessage); formStatus.appendChild(retryButton) retryButton.addEventListener('click', function() { // Section 4 formStatus.removeChild(errorMessage); formStatus.removeChild(retryButton); trySubmit(); }); }) }
Проблема не в том, что этот код уродлив. Мы не обсуждаем эстетику. Проблема в том, как вы будете искать здесь ошибку, если она возникнет.
Читайте также:
- Налаживаем отладку или улучшаем свой скилл дебаггинга
- Подробный гайд по отладке кода на JavaScript в Chrome Devtools
- Отладка программы: 3 типа ошибок
В зависимости от порядка отработки событий и обратных вызовов вы можете получить огромное количество возможных путей развития этой программы. В некоторых из них все хорошо. Но в других – несколько спиннеров, сообщения об ошибках и, возможно, сбои.
Здесь четыре секции кода и никакой гарантии о порядке их выполнения. На обычном калькуляторе можно посчитать, что есть 4х3х2х1=24 комбинации, в которых они могут сработать. А если добавить еще 4 сегмента, будет 8x7x6x5x4x3x2x1 - сорок тысяч комбинаций! Удачи в отладке :)
Другими словами, этот подход имеет сложность Bug O ?(n!). Здесь n – количество сегментов кода, изменяющих DOM. Да, это факториал. Безусловно, не все комбинации на практике могут возникнуть. С другой стороны, каждый из этих сегментов может выполняться более одного раза. Но даже с уточненным показателем Bug O сложность подобных программных интерфейсов слишком велика, можно сделать лучше.
Уменьшение отладочной сложности
Прежде всего нужно ограничить количество возможных состояний и результатов. Для этого не нужна библиотека – только продуманная структура кода.
let currentState = { step: 'initial', // 'initial' | 'pending' | 'success' | 'error' }; function trySubmit() { if (currentState.step === 'pending') { // запретить повторную отправку return; } setState({ step: 'pending' }); submitForm.then(() => { setState({ step: 'success' }); }).catch(error => { setState({ step: 'error', error }); }); } function setState(nextState) { // удалить всех существующих потомков formStatus.innerHTML = ''; currentState = nextState; switch (nextState.step) { case 'initial': break; case 'pending': formStatus.appendChild(spinner); break; case 'success': let successMessage = createSuccessMessage(); formStatus.appendChild(successMessage); break; case 'error': let errorMessage = createErrorMessage(nextState.error); let retryButton = createRetryButton(); formStatus.appendChild(errorMessage); formStatus.appendChild(retryButton); retryButton.addEventListener('click', trySubmit); break; } }
Этот вариант не сильно отличается от предыдущего, он даже чуть более многословен. Но отлаживать его намного проще – из-за этой строки:
function setState(nextState) { // удалить всех существующих потомков formStatus.innerHTML = ''; // ... добавить что-то в formStatus ...
Очистив контейнер перед выполнением каких-либо манипуляций, вы гарантируете, что DOM-операции всегда начинаются с нуля, энтропия не увеличивается и ошибки не накапливаются.
Если вдруг появится ошибка, достаточно вернуться всего лишь на один шаг назад – к предыдущему вызову setState
. Отладочная сложность такого интерфейса – ?(n), где n – количество сегментов, ответственных за рендеринг (тут их 4 – по количеству кейсов в switch
).
Проблемы еще могут возникнуть внутри функции trySubmit, но и здесь отладка гораздо проще. Можно зарегистрировать и проверить каждое промежуточное состояние и явно запретить любые нежелательные переходы.
function trySubmit() { if (currentState.step === 'pending') { // запретить повторную отправку return; }
Конечно, постоянное сбрасывание DOM – это компромисс. Удаление и воссоздание фрагмента страницы каждый раз разрушало бы его внутреннее состояние, вызывало потерю фокуса и проблемы с производительностью в больших приложениях.
В такой момент и нужны библиотеки вроде React. Они позволяют не воссоздавать пользовательский интерфейс с нуля без необходимости.
function FormStatus() { let [state, setState] = useState({ step: 'initial' }); function handleSubmit(e) { e.preventDefault(); if (state.step === 'pending') { // запретить повторную отправку return; } setState({ step: 'pending' }); submitForm.then(() => { setState({ step: 'success' }); }).catch(error => { setState({ step: 'error', error }); }); } let content; switch (state.step) { case 'pending': content = <Spinner />; break; case 'success': content = <SuccessMessage />; break; case 'error': content = ( <> <ErrorMessage error={state.error} /> <RetryButton onClick={handleSubmit} /> </> ); break; } return ( <form onSubmit={handleSubmit}> {content} </form> ); }
Код может выглядеть по-другому, но принцип сохраняется. Компоненты – это абстракции. Они гарантируют, что никакой другой код не испортит DOM или состояние вашей программы. Компонентный подход помогает уменьшить отладочную сложность программных интерфейсов – Bug O.
Если в DOM что-то не так, вы можете проследить путь ошибки, просмотрев последовательно дерево компонентов. Таким образом показатель сложности равен ?(высота этого дерева).
Читайте также:
- React Developer и путь его развития в 2019 году;
- Сравнение Angular и React и их ключевые особенности;
- Структурирование проекта на React – полное руководство.
Размышляя о проектировании программных интерфейсов, подумайте первым делом, какова их отладочная сложность ?. А что насчет уже имеющихся API, с которыми вы работаете? Redux, CSS, наследование – у всего есть своя Bug O стоимость.
Оригинал: The “Bug-O” Notation
Комментарии