спагетти код что это

Убираем спагетти-код

Два подхода к упорядочиванию хаоса.

Когда мы делали свой Трелло-планировщик из Бутстрапа и нашего списка задач, у нас появился спагетти-код. Это значит, что для простоты и скорости решения мы скопировали одинаковые куски кода, которые делают почти одно и то же. У них внутри одинаковая логика, но разные входные данные, и мы их просто скопировали-вставили.

У такого подхода к программированию есть несколько проблем:

Единственное, когда можно использовать такой подход — если нужно что-то быстро протестировать и понять, работает оно или нет. Если не работает — значит, мы сэкономили время и не стали тратить его на неудачное решение. А если всё работает как нужно, значит, настало время переписать код в нормальном виде. Для этого нам понадобится некоторое подобие объекта, но не на основе класса, а созданное вручную.

Мы хотели ООП, но не смогли

Изначально мы хотели на этом примере показать силу классов и объектов в объектно-ориентированном программировании. Но, разобравшись, поняли, что классы только всё усложнят, не принеся нам существенной пользы.

Поэтому мы будем использовать объекты, но без классов. А силу классов мы показали в игре на Питоне — посмотрите, вам понравится.

Новый тип переменной: объект

У переменной типа «объект» есть одна отличительная особенность: она состоит как бы из двух частей — свойства и значения. Мы про это говорили в статье про объекты в ООП, и здесь всё то же самое — у объекта меняются только значения свойств. Поясним на примере языка JavaScript.

Сделаем переменную-объект, у которой будет два свойства — имя и возраст:

Чтобы поменять имя, нужно написать название переменной и через точку указать название свойства, которое хотим изменить:

Свойств у объекта может быть сколько угодно:

Но если у объекта получается много однотипных свойств, можно сделать их массивом и обращаться к ним по номерам. А если элементы массива тоже сделать объектами с одинаковыми названиями свойств, получится вот такая магия:

Смотрите, так как у каждого элемента массива одинаковое название свойств, то, меняя просто номер элемента, мы можем запоминать разные свойства у одинаковых по сути элементов. Это нам пригодится на следующем шаге.

Собираем дубли

Пройдёмся по нашему старому коду из прошлой статьи и соберём все одинаковые переменные, которые отличаются только цифрами:

Первые две переменные задают ссылку на объект и маску для сравнения элементов, а вторые две отвечают за количество задач в колонках.

Свернём эти 16 переменных в один большой объект:

Теперь, зная только номер колонки, мы можем обратиться к объекту, маске и количеству задач в колонке. Сначала это может показаться громоздким, но на самом деле сокращает наш финальный объём программы в 4 раза.

Чтобы избавиться от дублей в коде, используют циклы или выносят повторяющийся код в отдельную функцию. На самом деле вариантов больше, но основные — эти.

Используем цикл

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

Сделаем то же самое в цикле, используя нашу большую переменную-объект. Для этого мы организуем цикл от 0 до 3 (потому что нумерация элементов массива начинается с нуля) и по очереди проверяем все значения:

Код сократился в 4 раза, читать стало проще, и всё меняется в одном месте. Красота.

Делаем отдельную функцию

У нас есть вот такой огромный кусок кода, который делает одно и то же, только с разными элементами.

Здесь 4 раза задаётся обработчик нажатия клавиш в поле ввода у каждой колонки. Очевидно, что проще вынести повторяющийся код в отдельную функцию и вызывать её по мере необходимости:

Что дальше

Мы только что убрали почти весь спагетти-код из нашего проекта. Почти — потому что у нас осталось два неопрятных фрагмента:

Оба фрагмента можно оптимизировать, например, с помощью jQuery.

Попробуйте сделать это сами, пока это не сделали мы 🙂

Источник

10 принципов хорошего кода и хорошего программиста

10 принципов хорошего кода и хорошего программиста

Спагетти-коды, огромные цепочки «if-else» и программы, которые ломаются после изменения переменной, функции размером в сотни строк и раздражающие имена переменных и классов? Это лишь некоторые из постоянно встречающихся в работе недочётов. И результат того, что будет, если попытаться превратить надвигающийся дедлайн в готовый продукт, внутри которого скрывается проблемный и перегруженный код.

Попробуем написать код, думая о его ремонтопригодности, а не только о том, чтобы он «работал!». Код, который поймёт и сможет поддерживать любой (разумеется любой, кто знаком с программированием). Будем следовать 10 принципам хорошего программиста, которые выведут вашу работу на новый уровень!

KISS расшифровывается как «чем проще — тем лучше». Этот принцип стоит использовать для решения разных жизненных задач, и он очень помогает в написании кода.

Например, вы собираетесь разработать мобильную игру — и у вас появился шанс это сделать. Не факт, что вы создадите следующую Candy Crush или Clash Royale. Поэтому постарайтесь оставить проект маленьким и простым. А если он уже прост — сделайте его ещё проще. Дополнительные функции появятся, когда вы сделаете стабильно работающую базу. Впрочем, можете не придерживаться этого правила, перегрузить софт функциями и «взорвать» весь проект.

Во время работы старайтесь сделать код простым, потому что сложный быстро становится трудоёмким. Чем больше времени вы тратите на одну программу — тем больше багов и ошибок получите. И в конечном итоге это приведёт к трудностям, если вы захотите изменить что-то в проекте позже.

DRY означает «не повторяйся», и это следует понимать буквально. Если ваш код повторяется и повторяет одни действия, вы нарушаете это правило. Несколько функций делают одно и то же? Рефакторинг в одну функцию. Содержат ли несколько переменных одни и те же данные? Рефакторинг в одну переменную.

Против принципа DRY выступает WET: «пиши всё дважды» или «потрать время зря». Основной вопрос для определения нарушителей правил заключается в следующем: сколько мест необходимо изменить, чтобы пофиксить программу? Если ваш ответ «больше одного», значит, вы действовали вопреки принципу DRY.

Представьте, что вы разрабатываете музыкальное приложение с тремя страницами: альбомы, названия и плейлисты. Для каждой из этих страниц существует собственный класс, а для каждого класса — своя функция «fetchMusic= ()». Так почему же в коде есть три места, которые ведут себя одинаково? Извлеките эту функцию и не действуйте против принципа DRY.

Открытый/Закрытый

Этот принцип хорошего программиста означает, что уже реализованные логические функции должны оставаться таковыми, и их нет смысла переписывать. В то же время новые требования или элементы можно добавлять к существующим классам, и расширять эти самые классы, а не менять. Так что они «открыты» для расширений, но «закрыты» для модификаций.

Это — ключ к созданию хорошего API, особенно если вы хотите выпустить библиотеку. Не соблюдайте этот принцип, и пользователи будут использовать вашу библиотеку «как есть», либо не использовать её вообще. И если вы захотите дать им возможность расширять её, они смогут менять ваш код в соответствии со своими потребностями. Вряд ли это то, чего вы добивались.

Если во время основного релиза кто-то серьёзно модифицирует код, требуется слияние всех изменений. Это занимает много времени и может привести к появлению багов. Не говоря уже о том, что те, кто переписывал код, наделают своих ошибок. Следование принципу «открытый/закрытый» защищает ваш софт от таких проблем, потому что простые расширения не могут взломать ни один существующий код.

Построение, а не наследование

Это означает, что поведение программ надо прописывать, а не брать со стороны. Почему? Потому что по мере роста древо наследования становится всё более запутанным, и каждая его «ветвь» получает свой собственный набор поведений. И попытки перенести модель поведения из одной «ветви» в другую могут оказаться трудными.

Прописанное с нуля поведение легче обрабатывать и поддерживать. Таким образом, также можно производить бесконечное количество моделей поведения. И из каждой комбинации можно получить свой класс.

Одним из примеров могут быть враги в видеоигре. На первом уровне они могут только бить. На следующем уровне — бить и пинать или бить и плеваться, но не пинать. Или плевать ядом и бить ногами, но не кулаком. Теперь представьте себе, что этот набор навыков растёт для каждого уровня. Попробуйте нарисовать дерево наследования, и вы скоро увидите, что построение с нуля гораздо удобнее.

Отдельная ответственность

Этот принцип хорошего программиста гласит, что каждый класс должен заботиться о предоставлении только одного бита.

Если вы придерживаетесь принципа KISS или у вашего проекта мало фишек, то большинство классов просто и без ошибок выполняют один тип работы за раз. Но с ростом требований к коду и приближением дедлайна большинство классов начинают делать по несколько вещей и нарушают этот принцип.

Чтобы придерживаться пятого правила, задайте себе вопрос, где и когда меняется каждая функция? Если ответ будет «более чем в одном месте и более чем по одной причине», вы нарушаете этот принцип.

Разделение задач

Этот принцип похож на предыдущий, но его стоит рассматривать на более высоком уровне абстракции. Программа состоит из нескольких частей, которые в лучшем случае не связаны друг с другом.

Хорошо известен пример MVVM-Pattern (Модель, Представление и Модель представления), где приложение разбивается на три не пересекающихся части. Модель данных содержит необработанные данные и выполняет некоторый алгоритм. Модель представления содержит агрегированные данные, которые должны отображаться, но не знает, как отобразить их. А Представление отображает эти данные наилучшим образом. Кроме того, только у него есть возможность взаимодействовать с пользователем и реагировать на него напрямую.

Таким образом, Модель представления и Модель не знают, на какую кнопку или область нажал пользователь. Представление обрабатывает действия человека и доставляет данные в Модель представления, которая выполняет собственные алгоритмы, не контактируя с пользователем. Это даёт возможность создавать модульные коды и параллельно разрабатывать каждую его часть.

YAGNI

Принцип хорошего программиста «Тебе это не нужно» хочет, чтобы вы не прописывали функции, которые могут и не понадобиться в будущем. Потому что велика вероятность, что они вам вообще не понадобятся. Зато усложнят код.

Можете рассматривать это как строгое соблюдение принципов DRY и KISS. В основном его нарушают неопытные разработчики. Они пишут очень абстрактный код и в итоге получают нечто раздутое и невозможное в использовании.

Избегайте преждевременной оптимизации

Смотрите на это как на противоположность принципа YAGNI. Первый призван, чтобы избежать реализации ненужных фрагментов кода, второй нужен, чтобы предотвратить слишком раннюю оптимизацию.

Вы не можете обнаружить недоработку, пока она не обнаружит вас! Программа должна сама показать вам «узкое место», потому что вы не найдете его, изучая код самостоятельно.

Рефракторинг, рефракторинг, рефракторинг

Многие знакомы с этим принципом хорошего программиста, но принимают не все: «код редко получается совершенным с первого раза» — Роберт К. Мартин.

Лучше ещё раз просмотреть и переработать сделанное ранее, потому что это помогает убедиться в правильности кода.Это нормально, такова природа вещей.

Если же вы посмотрели на старый код и поняли, что не можете его улучшить, есть только 2 варианта:

Я очень сомневаюсь, что кто-то может когда-либо прийти к варианту 1.

Поэтому возвращаться к старым участка кода и улучшать их — нормально. Особенно учитывая тот факт, что кодовые базы постоянно расширяются. Относитесь к этому принципу как к «правилу большого пальца» или как к рутине, но не забывайте совершенствовать свой код.

Чистый код лучше, чем «умный» код

Оставьте музу дома и напишите чистый код вместо шедеврального. Потому что шедевральный код — это загадка, которую будут решать ваши товарищи! Раскрою небольшой секрет: «умный код» довольно быстро станет загадкой и для вас. Никому нет дела до «умного» когда, если его невозможно прочесть.

«Умный код» делает как можно больше в одной строке вместо того, чтобы разбить всё для лучшего восприятия. Или содержит странные имена переменных/методов/классов вместо выразительных. Всё, что заставляет кого-то сказать «Подождите, что?», можно идентифицировать как «умный код».

Хороший программист пишет читаемый код и оставляет комментарии, если это действительно необходимо. Комментарии, которые объясняют, почему, а не как всё делается. Придерживайтесь руководств по стилю и пишите код, соответствующий языку.

Наконец, упомяну, что каждый разработчик программного обеспечения должен прочитать две книги, чтобы прокачать себя. Это «Чистый код» Роберта К. Мартина и «Совершенный код» Стива МакКоннелла. Прочитав их, я почувствовал себя заново рождённым в мире программирования, но сохранил опыт своей прежней жизни.

Хинт для программистов: если зарегистрируетесь на соревнования Huawei Cup, то бесплатно получите доступ к онлайн-школе для участников. Можно прокачаться по разным навыкам и выиграть призы в самом соревновании.

Перейти к регистрации

Источник

🚀 Объектно-ориентированное программирование – самая большая ошибка компьютерных наук

спагетти код что это. 15862a20de0cb641c38957722c271ef9. спагетти код что это фото. спагетти код что это-15862a20de0cb641c38957722c271ef9. картинка спагетти код что это. картинка 15862a20de0cb641c38957722c271ef9. Два подхода к упорядочиванию хаоса.

Почему же ООП так опасен?

Представьте, что вы садитесь в свою хорошо знакомую машину и едете по хорошо знакомому маршруту. И вдруг что-то идет не так, как всегда. Вы отпускаете педаль газа, но машина продолжает ускоряться! Давите на тормоз – не помогает!

Страшно? Подобный инцидент произошел в 2007 году с Джин Букаут – и еще с сотнями водителей Toyota Camry. Десятки человек погибли.

Производитель ссылался на залипание педалей, человеческий фактор и даже половые коврики, однако истинным виновником оказалось программное обеспечение автомобилей.

Команда экспертов 18 месяцев разбирала кодовую базу Toyota – и нашла сотни потенциальных причин возникновения непреднамеренного ускорения. Код в целом был запутанным и беспорядочным – то, что на сленге называется «спагетти». Из-за этого Toyota пришлось отозвать более 9 млн автомобилей и выплатить более 3 млрд долларов.

спагетти код что это. 8bdda824ef8d5f5d74bf88241f43d76f. спагетти код что это фото. спагетти код что это-8bdda824ef8d5f5d74bf88241f43d76f. картинка спагетти код что это. картинка 8bdda824ef8d5f5d74bf88241f43d76f. Два подхода к упорядочиванию хаоса.

Проблема спагетти

Подобные инциденты не уникальны – и это пугает. Например, два самолета Boeing 737 Max потерпели крушение из-за ошибки, вызванной спагетти-кодом (346 жертв, 60 млрд долларов ущерба). То же самое может случиться, например, на атомной электростанции или в реанимационной палате.

Как ни странно, программный код пишется прежде всего для людей. Мартин Фаулер говорил, что любой дурак может написать код, понятный компьютеру, а хорошие программисты пишут код, понятный людям. Если код непонятен, то очень скоро он перестанет работать.

Спагетти-код непонятен, связи между его частями не очевидны. Любое изменение может что-нибудь сломать. На такой код невозможно писать тесты, его сложно расширять и больно поддерживать.

Откуда берется спагетти-код?

Со временем любой код может превратиться в спагетти. По мере усложнения он становится все более и более запутанным, энтропия растет – и в один прекрасный момент вы уже имеете дело с клубком зависимостей.

Чтобы бороться с этим нужны строгие ограничения, вроде ограничения скорости на дорогах. Причем такие ограничения должны быть максимально автоматизированы и не зависеть от человеческого фактора. В идеале их должна накладывать сама парадигма программирования.

ООП – корень зла

ООП не накладывает на код никаких ограничений, которые могли бы предотвратить его запутывание. Безусловно, есть лучшие практики разработки – внедрение зависимостей, TDD, Domain Driven Design, которые реально помогают. Однако они не внедрены в парадигму, не обеспечиваются ей, и нет инструментария, который мог бы следить за их соблюдением.

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

Ссылочные типы

В большинстве объектно-ориентированных языков данные передаются по ссылке, то есть разные участки программы могут иметь дело с одной и той же структурой данных – и менять ее.

Но в современном ООП одни «клетки» проникают внутрь других и меняют их состояние. Это приводит к большой связанности кода. Изменения в одном месте программы могут привести к неожиданным последствиям в другом.

Предсказуемость

Склонность к спагеттификации – не единственная проблема ООП-парадигмы.

Если 2+2 будет равно 5 хотя бы один раз из миллиона, это может привести к ужасным последствиям.

Если эта цитата вам не нравится, то это потому, что недетерминированность в целом никуда не годится.

Вот пример кода, который просто вызывает функцию:

Мы не знаем, что делает эта функция, но кажется, что она всегда возвращает один и тот же результат для одних и тех же входных данных.

Эта функция вернула разные значения для одних и тех же входных параметров. Функция computeb недетерминирована. Она может выдавать ожидаемое значение, но это не гарантируется.

Что делает функцию детерминированной или недетерминированной?

Детерминированный код предсказуем, недетерминированный – нет.

Непредсказуемость

Давайте рассмотрим простую функцию сложения:

В большинстве языков программирования операция сложения реализуется на аппаратном обеспечении, то есть за нее отвечает очень надежный процессор. Поэтому мы всегда можем быть уверены, что функция add детерминирована.

Теперь немного усложним задачу – введем в бой объекты:

Пока все идет хорошо. Давайте внесем небольшое изменение в тело функции add :

Что случилось? Внезапно результат перестает быть предсказуемым! В первый раз код сработал нормально, но с каждым последующим запуском его вывод становился все более и более неожиданным.

Другими словами, функция больше не детерминирована.

Почему это вдруг произошло? Потому что функция начала вызывать побочные эффекты, изменив значение за пределами своей области действия.

Каковы последствия недетерминированного кода? В нем легко появляются дефекты – баги, которые заставляют разработчиков тратить драгоценное время на отладку и значительно ухудшают качество работы ПО.

Чтобы сделать наши программы более надежными, мы должны в первую очередь заняться их детерминизмом.

спагетти код что это. 59c03547abafa8c03be70f9695b7ca46. спагетти код что это фото. спагетти код что это-59c03547abafa8c03be70f9695b7ca46. картинка спагетти код что это. картинка 59c03547abafa8c03be70f9695b7ca46. Два подхода к упорядочиванию хаоса.

Побочные эффекты

Что такое побочный эффект? Если вы принимаете лекарство от головной боли, но оно вызывает у вас тошноту, то тошнота является побочным эффектом. Проще говоря, этот что-то нежелательное и не связанное с основной задачей препарата.

Вернемся к нашей функции сложения:

Чистота

Разобравшись с детерминизмом и побочными эффектами, мы готовы говорить о чистоте.

Чистая функция – это функция, которая одновременно детерминирована и не имеет побочных эффектов. То есть у нее всегда предсказуемый результат работы, и она не делает ничего лишнего.

Чистые функции имеют множество преимуществ:

И так далее. Проще говоря, чистые функции возвращают радость в программирование.

Насколько чисто ООП?

Для примера давайте поговорим о двух фичах ООП — геттерах и сеттерах.

Результат геттера зависит от внешнего состояния — состояния объекта. Многократный вызов геттера может привести к различным выходным данным, в зависимости от состояния системы. Это делает геттеры изначально недетерминированными.

Сеттеры предназначены для изменения состояния объекта, что делает их по своей сути побочными эффектами.

Таким образом, все методы в ООП (кроме, возможно, статических) либо не детерминированы, либо вызывают побочные эффекты. Следовательно, ООП – это что угодно, только не чистое программирование.

Чистое решение

Есть ли что-то, что может спасти программирование – луч надежды в мрачном мире программных сбоев? Что-то достаточно надежное и детерминированное, чтобы на нем можно было строить качественное ПО? Это математика.

спагетти код что это. 0edf6fbc3c29eb77ae15438c68040968. спагетти код что это фото. спагетти код что это-0edf6fbc3c29eb77ae15438c68040968. картинка спагетти код что это. картинка 0edf6fbc3c29eb77ae15438c68040968. Два подхода к упорядочиванию хаоса.

А на чем основано современного ООП? Уже не на биологических законах клеток, как планировал Алан Кей. Оно базируется на множестве нелепых идей вроде классов и наследования, склеенных скотчем, чтобы лучше держались.

Основной строительный блок функционального программирования – функция в ее математическом понимании. В большинстве случаев – чистая функция, детерминированная и предсказуемая. Программа, составленная из таких функций, тоже становится предсказуемой.

Значит ли это, что в такой программе нет багов? Конечно нет. Однако и баги в ней детерминированы. Для одних и тех же данных всегда будет возникать одна и та же ошибка, что существенно облегчает ее исправление.

Как я тут оказался?

Очень похожий процесс происходит сейчас с ООП. Только вместо вопроса «как я попал в эту точку исполнения», мы спрашиваем «как я попал в это состояние».

ООП (и императивное программирование в целом) не может нам ответить. Когда все передается по ссылке, любой объект может быть изменен другим объектом – и парадигма не накладывает никаких ограничений, чтобы это предотвратить. Инкапсуляция совсем не помогает – вызов метода для мутации некоторого поля объекта ничем не лучше, чем его непосредственная мутация. Программы быстро превращаются в месиво зависимостей и глобального состояния.

Функциональное программирование избавит нас от сложных вопросов. Когда состояние остается неизменным, оно не вызывает проблем.

А как же спагетти-код?

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

В функциональном программировании есть только композиция. Функции вызывают другие функции, большие функции состоят из малых. Это единственный способ построения программ. Иначе говоря, парадигма программирования сама навязывает лучшую практику, она является нативной, естественной. Когда у нас нет мешанины связей и зависимостей, разработка, тестирование и рефакторинг становятся удовольствием.

Но ООП и ФП дополняют друг друга!

Жаль вас разочаровывать, но это не так.

Объектно-ориентированное программирование – полная противоположность функциональному. Оно нарушает многие фундаментальные принципы:

Программисты ООП тратят большую часть своего времени на исправление ошибок. Программисты ФП – на написание работающего кода.

Действуйте, пока не поздно

ООП было очень большой и ужасно дорогой ошибкой. Давайте все, наконец, признаем это. Если ваш автомобиль работает на объектно-ориентированном ПО, вы не можете быть спокойны.

Пришло время принять меры. Мы все должны осознать опасность ООП и начать делать маленькие шаги в сторону функционального программирования. Это не быстрый процесс, реального сдвига можно ожидать не раньше, чем через 10 лет, однако он должен произойти.

В ближайшем будущем ООП-программисты станут «динозаврами», как сейчас разработчики на COBOL. C++, Java, C# умрут. TypeScript тоже умрет.

Источник

Автоматы против спагетти-кода

спагетти код что это. image loader. спагетти код что это фото. спагетти код что это-image loader. картинка спагетти код что это. картинка image loader. Два подхода к упорядочиванию хаоса.

«Люблю спагетти-вестерны, ненавижу спагетти-код»

«Спагетти-код» — это идеальное выражение для описания ПО, представляющего собой дымящийся хаос и с когнитивной, и с эстетической точки зрения. В этой статье я расскажу о плане из трёх пунктов по уничтожению спагетти-кода:

Сложные задачи — это сложные задачи, и обычно ничего, кроме революционного открытия, не способно их упростить. Однако так бывает, что сама структура ПО добавляет ненужную сложность, и эту проблему стоит решать.

Уродливость спагетти-кода заключается в его сложной условной логике. И хотя жизнь может быть сложно представить без множества хитрых конструкций if-then-else, эта статья покажет вам решение получше.

спагетти код что это. image loader. спагетти код что это фото. спагетти код что это-image loader. картинка спагетти код что это. картинка image loader. Два подхода к упорядочиванию хаоса.

Чтобы проиллюстрировать ситуацию со спагетти-кодом, нам нужно для начала превратить это:

спагетти код что это. cf9eac43c588d9fecbbfc9bf15753ee1. спагетти код что это фото. спагетти код что это-cf9eac43c588d9fecbbfc9bf15753ee1. картинка спагетти код что это. картинка cf9eac43c588d9fecbbfc9bf15753ee1. Два подхода к упорядочиванию хаоса.

спагетти код что это. 7fc683f12aa20bcf4abcf5a774c7a039. спагетти код что это фото. спагетти код что это-7fc683f12aa20bcf4abcf5a774c7a039. картинка спагетти код что это. картинка 7fc683f12aa20bcf4abcf5a774c7a039. Два подхода к упорядочиванию хаоса.

Давайте приступим к готовке.

Неявное состояние

Чтобы приготовить пасту, нам совершенно точно понадобится вода для варки. Однако даже такой кажущийся простым элемент при участии спагетти-кода может оказаться очень запутанным.

Вот простой пример:

Что на самом деле делает эта проверка? Очевидно, она делит числовую прямую на две части, но что означают эти части? Думаю, вы можете сделать логичное предположение, но проблема в том, что код на самом деле не сообщает этого явно.

Если я действительно подтвержу, что она проверяет, является ли вода ТВЁРДОЙ [прим. пер.: по шкале Фаренгейта вода замерзает при +32 градусах], то что будет логически означать возвращаемое false?

Хотя проверка разделила числа на две группы, на самом деле существует три логических состояния — твёрдое тело, жидкость и газ (SOLID, LIQUID, GAS)!

То есть эта числовая прямая:

спагетти код что это. image loader. спагетти код что это фото. спагетти код что это-image loader. картинка спагетти код что это. картинка image loader. Два подхода к упорядочиванию хаоса.

разделена проверкой условия следующим образом:

спагетти код что это. image loader. спагетти код что это фото. спагетти код что это-image loader. картинка спагетти код что это. картинка image loader. Два подхода к упорядочиванию хаоса.

спагетти код что это. image loader. спагетти код что это фото. спагетти код что это-image loader. картинка спагетти код что это. картинка image loader. Два подхода к упорядочиванию хаоса.

Заметьте, что произошло, потому что это очень важно для понимания природы спагетти-кода. Булева проверка разделила числовое пространство на две части, но НЕ категоризировала систему как настоящую логическую структуру из (SOLID, LIQUID, GAS). Вместо этого проверка разделила пространство на (SOLID, всё остальное).

Вот похожая проверка:

Визуально это будет выглядеть так:

спагетти код что это. image loader. спагетти код что это фото. спагетти код что это-image loader. картинка спагетти код что это. картинка image loader. Два подхода к упорядочиванию хаоса.

спагетти код что это. image loader. спагетти код что это фото. спагетти код что это-image loader. картинка спагетти код что это. картинка image loader. Два подхода к упорядочиванию хаоса.

спагетти код что это. image loader. спагетти код что это фото. спагетти код что это-image loader. картинка спагетти код что это. картинка image loader. Два подхода к упорядочиванию хаоса.

Мне всё равно никогда не нравился твой код

Показанный выше код подразумевает существование трёх состояний вещества — SOLID, LIQUID, GAS. Однако согласно научным данным на самом деле есть четыре наблюдаемых состояния, в которые включается плазма (PLASMA) (на самом деле есть и множество других, но нам будет достаточно и этого). Хотя никто не готовит пасту из плазмы, если этот код будет опубликован на Github, а затем его форкнет какой-нибудь аспирант, изучающим физику высоких энергий, то нам придётся поддерживать и это состояние тоже.

Однако при добавлении плазмы показанный выше код наивным образом будет выполнять следующее:

Вполне вероятно, что старый код при добавлении ко множеству состояний плазмы сломается в ветвлениях else. К сожалению, ничто в структуре кода не помогает сообщить о существовании нового состояния или повлиять на изменения. Кроме того, любые баги скорее всего окажутся малозаметными, то есть, найти их будет сложнее всего. Просто скажи «нет» насекомым в спагетти.

Если вкратце, то проблема заключается в следующем — булевы проверки используются для определения состояний косвенно. Логические состояния часто не объявляются и не видны в коде. Как мы увидели выше, когда система добавляет новые логические состояния, существующий код может сломаться. Чтобы избежать этого, разработчики должны заново изучить каждую отдельную условную проверку и ветвление, чтобы убедиться, что пути кода по-прежнему верны для всех соответствующих им логических состояний! Это основная причина деградации больших фрагментов кода при их усложнении.

Хоть и не существует способов полностью избавиться от условных проверок данных, любая минимизирующая их техника будет снижать сложность кода.

Давайте теперь рассмотрим типичную объектно-ориентированную реализацию класса, создающую очень простую модель объёма воды. Класс будет управлять изменениями состояния вещества воды. Изучив проблемы классического решения этой задачи, мы затем обсудим новую нотацию под названием Frame и покажем, как она может справляться с обнаруженными нами трудностями.

Сначала доведите воду до кипения…

Наука дала названия всем возможных переходам, которые может совершать вещество при изменении температуры.

спагетти код что это. image loader. спагетти код что это фото. спагетти код что это-image loader. картинка спагетти код что это. картинка image loader. Два подхода к упорядочиванию хаоса.

Наш класс очень прост (и не особо полезен). Он отвечает на вызовы выполнения переходов между состояниями и изменяет температуру, пока она не станет подходящей для требуемого целевого состояния:

(Примечание: этот псевдокод написал я. Пользуйтесь им в работе только на свой страх и риск.)

По сравнению с первым примером этот код имеет определённые улучшения. Во-первых, жёстко заданные «магические» числа (32, 212) заменены на константы границ температур состояний (WATER_SOLID_TEMP, WATER_GAS_TEMP). Это изменение начинает делать состояния более явными, хотя и косвенным образом.

В этом коде также появляются проверки «защитного программирования», которые ограничивают вызов метода, если он находится в неподходящем для операции состоянии. Например, вода не может замерзать, если она не является жидкостью — это нарушает закон (природы). Но добавление сторожевых условий усложняет понимание назначения кода. Например:

Эта условная проверка делает следующее:

Во-вторых, код проверяет, не является ли вода жидкой, чтобы узнать, не нужно ли возвращать ошибку.

С первого раза разобраться в этом двойном отрицании состояний совсем непросто. Вот упрощение, которое немного снижает сложность выражения:

Этот код понять проще, потому что состояние isLiquidWater выражено явно.

Теперь мы исследуем техники, закрепляющие явное состояние как наилучший способ решения задач. При таком подходе логические состояния системы становятся физической структурой ПО, что улучшает код и упрощает его понимание.

Frame Machine Notation

Frame Machine Notation (FMN) — это предметно-ориентированный язык (Domain Specific Language, DSL), определяющий категоричный, методологический и простой подход к заданию и реализации различных типов автоматов. Для простоты я буду называть автоматы Frame просто «машинами», потому что эта нотация может определять теоретические критерии для любых отличающихся типов (машин состояний, магазинных автоматов и вершины эволюции автоматов — машин Тьюринга). Чтобы знать о различных видах автоматов и их применении, рекомендую изучить страницу в Википедии.

Хотя теория автоматов может быть интересной (ОЧЕНЬ сомнительное заявление), в этой статье мы сосредоточимся на практическом применении этих мощных концепций для создания систем и написания кода.

Для решения этой задачи Frame вводит стандартизированную нотацию, работающую на трёх интегрированных уровнях:

Frame — это нотация, отличающаяся некоторыми важными аспектами:

Сейчас я познакомлю вас с двумя самыми важными объектами первого уровня в Frame — Frame Events и Frame Controllers.

Frame Events

FrameEvents являются неотъемлемой частью простоты нотации FMN. FrameEvent реализуется как структура или класс, которые минимум имеют следующие переменные-члены:

В нотации Frame используется символ @, идентифицирующий объект FrameEvent. Каждый из обязательных атрибутов FrameEvent имеет специальный токен для доступа к нему:

Часто нам не обязательно указывать то, с чем работает FrameEvent. Так как большинство контекстов одновременно работают только с одним FrameEvent, нотацию однозначно можно упростить так, чтобы она использовала только селекторы атрибутов. Следовательно, мы можем упростить доступ:

Такая нотация поначалу может показаться странной, но вскоре мы увидим, как такой простой синтаксис для событий сильно упрощает понимание кода FMN.

Frame Controllers

Frame Controller — это объектно-ориентированный класс, упорядоченный чётко заданным образом для реализации машины Frame. Типы контроллеров идентифицируются префиксом #:

это эквивалентно следующему объектно-ориентированному псевдокоду:

Очевидно, что этот класс не особо полезен. Чтобы он мог что-то делать, контроллеру нужно хотя бы одно состояние для ответа на события.

Контроллеры структурируются таким образом, чтобы содержать блоки различного типа, которые идентифицируются по тире, окружающим имя типа блока:

В контроллере может быть не более одного экземпляра каждого блока, а типы блоков могут содержать только определённые типы подкомпонентов. В этой статье мы исследуем только блок -machine-, который может содержать только состояния. Состояния идентифицируются по токену префикса $.

Здесь мы видим FMN для контроллера, содержащего машину только с одним состоянием:

Вот реализация приведённого выше кода FMN:

Реализация блока machine состоит из следующих элементов:

После задания этих основ реализации блока machine, мы можем увидеть, как хорошо объект FrameEvent взаимодействует с машиной.

Блок интерфейса

Взаимодействие FrameEvents, управляющих работой машина — это сама суть простоты и мощи нотации Frame. Однако мы ещё не ответили на вопрос, откуда же берутся FrameEvents — как они попадают в контроллер, чтобы управлять им? Один из вариантов: внешние клиенты сами могут создавать и инициализировать FrameEvents, а затем напрямую вызывать метод, на который указывает переменная-член _state:

Гораздо лучшей альтернативой будет создание общего интерфейса, оборачивающего прямой вызов переменной-члена _state:

Однако наиболее беспроблемный способ, соответствующий обычному способу создания объектно-ориентированного ПО — это создание общих методов, отправляющих событие от лица клиента во внутреннюю машину:

Frame определяет синтаксис для блока интерфейса, который содержит методы, превращающие вызовы в общий интерфейс для FrameEvents.

У блока interface ещё много других особенностей, но этот пример даёт нам общее представление о том, как это работает. Дальнейшее объяснение я дам в следующих статьях серии.

Теперь давайте продолжим изучение работы автомата Frame.

Обработчики событий

Хоть мы и показали, как определять машину, у нас пока нет нотации с которой можно что-нибудь делать. Для обработки событий нам нужно 1) иметь возможность выбора события, которое нужно обрабатывать и 2) привязать его к выполняемому поведению.

Вот простой контроллер Frame, обеспечивающий инфраструктуру для обработки событий:

Как сказано выше, для доступа к атрибуту _msg события FrameEvent нотация FMN использует скобки из вертикальных линий:

FMN также использует токен знака возведения в степень, обозначающий оператор возврата. Показанный выше контроллер будет реализован следующим образом:

Здесь мы видим, насколько чётко нотация FMN соответствует паттерну реализации, который удобен в понимании и кодинге.

Задав эти базовые аспекты событий, контроллеров, машин, состояний и обработчиков событий, мы можем перейти к решению с их помощью реальных задач.

Однофокусные машины

Выше мы рассматривали контроллер без состояний, который был довольно бесполезным.

На одну ступеньку выше в пищевой цепочке полезности находится класс с единственным состоянием, который, хоть и не бесполезен, попросту скучен. Но, по крайней мере, он хотя бы что-то делает.

Для начала давайте посмотрим, как будет реализован класс со всего одним (подразумеваемым) состоянием:

Здесь не объявляется и даже не подразумевается никакого состояния, но давайте предположим, что если код что-то делает, то система находится в состоянии «Working».

Визуализировать эту ситуацию можно с помощью таблицы привязок событий:

спагетти код что это. image loader. спагетти код что это фото. спагетти код что это-image loader. картинка спагетти код что это. картинка image loader. Два подхода к упорядочиванию хаоса.

Теперь давайте рассмотрим FMN, которая демонстрирует тот же функционал и соответствует той же таблице привязок:

Вот как выглядит реализация:

Можно заметить, что мы также ввели новую нотацию для оператора return, которая обозначает вычисление выражения и возврат результата интерфейсу:

Этот оператор эквивалентен

Включаем плиту

Пока мы видели контроллер с 0 состояний и контроллер с 1 состоянием. Они ещё не особо полезны, но мы уже на пороге чего-то интересного.

Чтобы сварить нашу пасту, нужно для начала включить плиту. Ниже показан простой класс Switch с единственной булевой переменной:

Хоть с первого взгляда это и не очевидно, но показанный выше код реализует следующую таблицу привязок событий:

спагетти код что это. image loader. спагетти код что это фото. спагетти код что это-image loader. картинка спагетти код что это. картинка image loader. Два подхода к упорядочиванию хаоса.

Для сравнения, вот FMN для того же поведения:

Теперь мы видим, насколько точно нотация Frame соответствует цели нашего кода — привязке события (вызова метода) к поведению на основании состояния, в котором находится контроллер. Кроме того, структура реализации также соответствует таблице привязок:

Таблица позволяет быстро понять предназначение контроллера в различных его состояниях. И структура нотации Frame, и паттерн реализации имеют схожие преимущества.

Изменяем состояние

Оператор изменения состояния выглядит следующим образом:

А вот соответствующая таблица привязки событий:

спагетти код что это. image loader. спагетти код что это фото. спагетти код что это-image loader. картинка спагетти код что это. картинка image loader. Два подхода к упорядочиванию хаоса.

Новое событие |toggle| теперь запускает изменение, которое просто циклически переключается между двумя состояниями. Как же можно реализовать операцию изменения состояния?

Проще некуда. Вот реализация Switch2:

Можно также внести в Switch2 последнее улучшение, чтобы он не только позволял переключаться между состояниями, но и явным образом задавать состояние:

В отличие от события |toggle|, если |turnOn| передаётся, когда Switch3 уже включен или |turnOff|, когда он уже выключен, то сообщение игнорируется и ничего не происходит. Это небольшое улучшение даёт клиенту возможность явно указывать состояние, в котором должен находиться переключатель:

Последний этап эволюции нашего переключателя показывает, насколько просто будет понимать предназначение контроллера FMN. Соответствующий код демонстрирует, насколько просто его реализовать с помощью механизмов Frame.

Создав автомат Switch, мы можем включить огонь и начать готовку!

Зондируем состояние

Ключевой, хоть и малозаметный, аспект автоматов заключается в том, что текущее состояние машины является результатом или ситуации (например, включение) или какого-то анализа данных или окружения. Когда машина переключилась в нужное состояние, подразумевается. что ситуация не изменится без ведома машины.

Однако это предположение не всегда верно. В некоторых ситуациях требуется проверка (или «зондирование») данных для определения текущего логического состояния:

В нашей ситуации такой метод можно реализовать следующим образом:

Как мы видим, метод просто возвращает ссылку на функцию состояния, соответствующего верному логическому состоянию. Эту функцию зондирования можно затем использовать для перехода в верное состояние:

Механизм реализации выглядит так:

Метод зондирования состояния — это пример нотации Frame для управления состоянием заданным образом. Далее мы также изучим важную нотацию для управления FrameEvents.

Наследование поведений и диспетчер

Наследование поведений и диспетчер — это мощная парадигма программирования и последняя тема о нотации Frame в данной статье.

Frame использует использует наследование поведений, а не наследование данных или других атрибутов. Для этого состояния отправляют FrameEvents другим состояниям, если исходное состояние не обрабатывает событие (или, как мы увидим в следующим статьях, просто хочет передать его дальше). Эта цепочка передачи событий может уходить на любую нужную глубину.

Для этого машины можно реализовывать с помощью техники под названием method chaining. Нотация FMN для передачи события из одного состояния в другое — это диспетчер =>:

Этот оператор FMN можно реализовать следующим образом:

Теперь мы видим, насколько просто объединять в цепочки методы состояний. Давайте применим эту технику к достаточно сложной ситуации:

спагетти код что это. image loader. спагетти код что это фото. спагетти код что это-image loader. картинка спагетти код что это. картинка image loader. Два подхода к упорядочиванию хаоса.

Благодаря изученным нами техникам реализация будет очень простой:

Машина для воды

спагетти код что это. image loader. спагетти код что это фото. спагетти код что это-image loader. картинка спагетти код что это. картинка image loader. Два подхода к упорядочиванию хаоса.

Вот как выглядит полная реализация FMN:

А теперь реализация:

Заключение

Автоматы — это базовая концепция компьютерных наук, которую слишком долго использовали только в специализированных областях разработки ПО и оборудования. Основная задача Frame заключается в создании нотации для описания автоматов и задания простых паттернов написания кода или «механизмов» для их реализации. Я надеюсь, что нотация Frame изменит взгляд программистов на автоматы, обеспечив простой способ их практического применения в повседневных задачах программирования, и, разумеется, позволит избавить их от спагетти в коде.

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *