4 шага к совершенству: правила для идеальных функций |
||
МЕНЮ Искусственный интеллект Поиск Регистрация на сайте Помощь проекту Архив новостей ТЕМЫ Новости ИИ Искусственный интеллект Разработка ИИГолосовой помощник Городские сумасшедшие ИИ в медицине ИИ проекты Искусственные нейросети Слежка за людьми Угроза ИИ ИИ теория Внедрение ИИКомпьютерные науки Машинное обуч. (Ошибки) Машинное обучение Машинный перевод Нейронные сети начинающим Реализация ИИ Реализация нейросетей Создание беспилотных авто Трезво про ИИ Философия ИИ Big data Работа разума и сознаниеМодель мозгаРобототехника, БПЛАТрансгуманизмОбработка текстаТеория эволюцииДополненная реальностьЖелезоКиберугрозыНаучный мирИТ индустрияРазработка ПОТеория информацииМатематикаЦифровая экономика
Генетические алгоритмы Капсульные нейросети Основы нейронных сетей Распознавание лиц Распознавание образов Распознавание речи Техническое зрение Чат-боты Авторизация |
2020-10-11 19:00 Функции — это блоки кода, выполняющие требуемые действия. Они являются фундаментальными составляющими любого проекта разработки. Без них мы не сможем ни обработать данные, ни представить их должным образом. Грамотное объявление функций считается столь необходимым навыком в арсенале программиста, что вам придется довольно долго практиковаться, прежде чем вы сможете с уверенностью сказать, что достигли высот в создании хорошо читаемых и удобных в обслуживании функций. Вполне вероятно, что разные программисты способны разработать собственные технологии написания функций. Более того, различные языки программирования отличаются друг от друга синтаксисом, поэтому нет смысла писать руководство, применимое только к одному из них. Вместо этого я решил поделиться с вами 4 универсальными правилами для написания хорошо читаемых и обслуживаемых функций. Надеюсь, что данные принципы помогут вам улучшить этот навык и применить его в любом предпочитаемом языке программирования. 1. Записывайте назначение функций Прежде чем приступить к написанию функции, необходимо определить, что же вам нужно. Программисты не прописывают текущую задачу для следующей функции, тем самым допуская самую распространенную ошибку. Я не утверждаю, что вы совсем не задумываетесь о назначении новой используемой функции, а лишь хочу обратить ваше внимание на то, что вам следует описывать в нескольких словах ожидаемые результаты ее действия. Возможно, некоторые искушенные программисты поспешат мне возразить, аргументируя тем, что зачастую у них нет необходимости делать какие-либо заметки перед написанием совершенного образца. И нет никаких сомнений в истинности их слов: они действительно так работают. Кому захочется подробно обосновывать причину использования функции для выполнения простой задачи? Честно говоря, и я не всегда напрямую следую этому правилу в отношении элементарных случаев. Однако эти опытные специалисты не осознают, что косвенно они всегда определяют свои задачи, давая функциям разумные имена. Ведь содержательное имя, а также четко сформулированный тип возвращаемых данных, ясно отражают цель объявляемой функции. К сожалению, не все владеют искусством правильно именовать функции и верно определять выходные данные. Приведем простой пример. func getCurrentTime() -> Date { Говорят, что стиль письма отражает образ мышления, и это в полной мере осознаешь, когда начинаешь писать больше обычного. Требуя от себя описания назначения функции, вы столкнетесь с необходимостью развивать свое логическое мышление, чтобы четко понимать, к какой функциональности вы стремитесь. Другими словами, вы будете точно знать, с какими данными работаете или что хотите получить в результате. А если речь идет о более сложных функциях, то знание итогового вывода/конечной точки (при условии, что функция ничего не возвращает) становится таким же необходимым, как информация о конечном пункте дистанции на старте марафонского забега. А вот и пример из реальной жизни. Как-то я работал над проектом анализа данных с помощью pandas, популярной библиотеки Python для различных операций с данными. На одном из этапов необходимо было объединить отдельные файлы CSV в единый датафрейм (табличную структуру данных в pandas). С этой целью я написал функцию Функция Python (от CSV к датафрейму) # Эта функция объединит отдельные файлы CSV в указанной папке и сформирует единый датафрейм Рассматривая данный пример, бегло перечислим порядок действий, необходимых на первом этапе определения функции. Основная мысль сводится к тому, что вы должны понимать, каких результатов хотите достичь с ее помощью:
2. Изучайте существующие решения Допустим, теперь вы абсолютно уверены в назначении своей функции, но не стоит спешить приступать к ее написанию. На своем горьком опыте я убедился, что поспешность в этом деле, особенно применительно к довольно сложным функциям, ни к чему хорошему не приводит, тем более если вы программист-самоучка. Одно из распространенных заблуждений некоторых программистов, независимо от их опыта работа, состоит в том, что им приходится или нравится самим писать все необходимые функции. Однако наступает момент, когда мы отчетливо понимаем, что тратим впустую так много нашего ценного времени. В чем же проблема? И о какой ошибке идет речь? Погружаясь в процесс написания функций, мы часто не удосуживаемся проверить наличие уже разработанных альтернативных решений либо в стандартных встроенных фреймворках языка программирования, либо в библиотеках с открытым ПО. Например, моя работа связана с областью биомедицинских исследований, где принято хранить все данные в формате CSV. В файлах этого формата столбцы выступают в виде имен переменных, а строки — записей данных об изучаемых объектах. На самом первом этапе процесса обработки данных нам необходимо прочитать CSV файлы, как раз это требование к функции мы учились определять в предыдущем разделе. Если мы заранее не подумаем о существующих решениях, то в результате получим функцию, как в нижеуказанном примере. Собственно говоря, мы будем использовать базовую функцию, которая читает текстовый файл и последовательно анализирует строки данных. Для упрощения примера предположим, что у данных CSV нет заголовков. Итак, пример функции Python (от CSV к словарю): # Эта функция прочитает файл CSV, обработает и сохранит данные в виде словаря для последующего анализа В данном случае наша ошибка в том, что мы не изучили и не использовали существующие решения, которые обычно намного более продуманны. Для той же самой задачи (чтение файла CSV с целью анализа данных) следует рассмотреть опции, доступные в библиотеке pandas, при помощи которых функцию можно переписать указанным ниже способом. Конечно, мы изменим наши выходные данные, так как тип данных датафрейма намного больше подходит для последующих этапов работы с ними. Разве функция Python (от CSV к датафрейму) не стала более читаемой? # Эта функция прочитает файл CSV и сохранит данные в формате датафрейма pandas для последующего анализа Этот пример демонстрирует простую функцию импорта данных. Но в реальных проектах куда чаще встречаются ситуации, в которых существующие решения оказываются лучше. Такие варианты функциональности взяли на себя часть тяжелой работы программистов. Наша же функция должна быть нацелена на решение неких специфичных задач для области контента, в которой мы работаем. Однако я вовсе не утверждаю, что в работе над проектами вам следует активно задействовать многочисленные сторонние библиотеки. Вместо этого я бы посоветовал быть открытым к новым идеям и учитывать вероятность существования хорошего альтернативного решения. Даже если вам не придется использовать подобные альтернативы, они могут натолкнуть вас на интересные идеи, которые помогут справиться со сложной задачей. 3. Создание прототипа функции Проанализировав всё вышесказанное, мы делаем вывод о необходимости написания функции. Поэтому пора создавать прототип. Но прежде стоит отметить, что в действительности речь идет не о простых функциях. В таких случаях мы способны написать код, не обращаясь к силе нашего логического мышления и структурной организации. Напротив, здесь под прототипированием мы понимаем разработку опытной версии более сложной функции (вследствие ее усложненной логики или использования многочисленных компонентов). Допустим, мы создаем представление профиля пользователя для приложения iOS. С этой целью мы воспользуемся структурой struct User { Предлагаю рассмотреть псевдокод Swift, демонстрирующий процесс создания представления профиля пользователя, а затем продолжить обсуждение прототипов функций: import UIKit // Эта функция создает представление профиля для данного пользователя Как видно из примера, речь идет о функции
4. Рефакторинг функции Создав рабочую версию функции, вы можете приступать к рефакторингу для улучшения ее организации и, следовательно, читаемости. Этот этап крайне важен, так как позволяет значительно повысить удобство обслуживания функции и связанного с ней кода. И именно этот шаг чаще всего пропускает большинство начинающих программистов. Как только у них что-то заработало, они считают, что цель достигнута. Но как же они ошибаются! Обращаясь к рассмотренному выше примеру, подумаем, как можно провести рефакторинг кода. Сначала создадим вспомогательные функции, которые будут использованы в обновленной версии Суть единственного принципа рефакторинга функции сводится к тому, чтобы придать ей компактный вид. Если быть более точным, то после него мы рассчитываем на уменьшение размера всех функций за счет того, что каждая из них отвечает за выполнение одной задачи. Например, функция
struct User { extension UILabel { extension String { С этими вспомогательными методами данный фрагмент кода отображает обновленную версию функции // Эта функция создает представление профиля определенного пользователя let usernameLabel = UILabel.formattedLabelForProfile() let biostatementLabel = UILabel.formattedLabelForProfile() let followerNumberLabel = UILabel.formattedLabelForNumbers() let followingNumberLabel = UILabel.formattedLabelForNumbers() // Настройка макета для подмножеств представлений элементов согласно ситуации Еще раз обращаю ваше внимание на один весьма важный момент — все вспомогательные функции имеют содержательные имена. Благодаря этому, желающим посмотреть код (или вам самим при возвращении к нему для обслуживания) даже не придется проверять реализацию этих функций, поскольку их имена отчетливо информируют о предполагаемом назначении. Фактически мы и дальше можем осуществлять рефакторинг Функция после рефакторинга (альтернатива): func createProfileView(user: User) -> ProfileView { //... func setupUserImageView() {} Как видно из этого примера, мы создали больше функций для отдельных компонентов UI. Важно то, что все они небольшого размера и выполняют одну задачу, что, как вы помните, отражает принцип их рефакторинга! Сравнив эти две преобразованные функции, мы сможем приобрести один важный навык для достижения лучшей читаемости и удобства обслуживания — структурную согласованность. Что же означает данное понятие в целом? В теле функции все подкомпоненты должны иметь схожие структуры. В первой версии преобразованной функции все элементы UI настраиваются внутри нее и обладают одинаковой длинной. Во второй версии элементы UI настраиваются по отдельности в соответствующих функциях, которые последовательно вкладываются в преобразованную. Уловили суть? Независимо от того, на какой версии вы остановитесь, следует уделить особое внимание структурной согласованности. Итоговая версия должна состоять из требуемого числа функций, а точнее компактных функций, нацеленных на выполнение одной задачи. После рефакторинга следует обязательно повторить выполнение функции, чтобы исключить возможность сбоев. Заключение В данной статье были рассмотрены 4 правила, руководствуясь которыми, можно писать хорошо читаемые и обслуживаемые функции. Как я уже говорил в самом начале, придется много и хорошо практиковаться, прежде чем вам удастся написать более совершенные функции или более чистый код. Но если вы будете помнить об этих полезных правилах (например содержательных именах и структурной согласованности), то вы добьетесь этого гораздо раньше. Читайте также:
Источник: m.vk.com Комментарии: |
|