Написание эффективного кода


Содержание

Теория чистого кода. Стиль кодирования

Чистый код должен быть эффективным, простым для восприятия и сопровождения, гибким и надежным. Приведенные требования зачастую противоречат друг другу, поэтому для написания чистого кода в каждом конкретном случае надо идти на некоторый компромисс. Нередко опытные программисты пытаются сформулировать советы по написанию чистого кода [1, 2, 3, 4, 5], которые зависят от используемого языка программирования, но во многом сходятся.

Эта статья изначально планировалась как своеобразная критика книги «Чистый код. Создание, анализ и рефакторинг» Роберта Мартина [1], поэтому я часто буду на него ссылаться. Мартин писал наиболее общие советы безотносительно конкретного языка программирования — этим его книга в корне отличается от других [2, 3, 4].

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

Содержание:

Эволюция требований к чистому коду

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

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

Выработаны архитектурные конструкции, правильное использование которых упрощает проектирование, восприятие и сопровождение программ — шаблоны проектирования. Шаблоны имеют широкое распространение и хорошо описаны в литературе — если программист, читая исходный код увидит слово Factory, то он заранее знает чего ждать от такого кода. Для каждого шаблона проанализированы сильные и слабые стороны, поэтому их использование предпочтительнее велосипедостроения.

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

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

  • не должен мешать программисту вносить изменения;
  • должен легко тестироваться;
  • должны использоваться, по возможности, стандартные решения.

Соглашения о кодировании

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

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

В соглашении обычно фиксируется использование множества специфичных для конкретного языка программирования конструкций. Например, для языка С++ может быть закреплено обязательное использование ключевого слова const везде где это возможно (это позволяет переносить обнаружение ряда ошибок на этап компиляции), запрет использования директив pragma (они могут оказать негативное влияние на переносимость программ) или, например, требование использовать, по возможности, const вместо define.

Отступы

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

  • Единый стиль оформления кода во всем проекте;
  • Визуальное выделение наиболее значимых частей.

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

В качестве примера я взял первую попавшуюся студенческую поделку — решаемая задача стоит в том, чтобы заполнить массив случайными числами, а затем обработать его элементы по заданной формуле:

Четвертая строка смещена вправо — возможно автор сделал это намеренно, чтобы нас о чем-то предупредить, но скорее всего, смещение произошло из за того, что при форматировании вперемешку использовались пробелы и символы табуляции. Современные среды разработки программ позволяют настроить автоматическую замену табуляций пробелами для решения указанной проблемы. Аналогичная проблема наблюдается в 15 строке.

Смещение тела цикла (7-11 строки) произошло, вероятно, в следствии модификации кода — изначально это был вложенный цикл, но затем студент что-то исправил, но форматирование сохранил. В маленькой функции такое отсутствие форматирования не сильно мешает, но в более серьезном коде, оно отвлекает, расходует ваше время и портит зрение. В конце концов, многие IDE имеют горячие клавиши, для форматирования фрагмента кода по заранее заданным правилам — достаточно один раз настроить среду разработки чтобы всегда экономить время.

В приведенном коде есть множество других недочетов:

  • в 9 строке выполняется приведение типа в функциональном стиле (тип используется как функция) и после открывающей скобки стоит пробел, но в 14 строке — при вызове функции pow, пробел не поставлен. Возможно, автор лишний пробел поставил умышленно и человек, читающий код, обратит на это внимание;
  • лишний пробел стоит перед объявлением переменных в третьей строке;
  • оператор присваивания в 9 строке выделен пробелами, но в 10 — пробелы отсутствуют.

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

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

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

В 16 строке вокруг оператора умножения нет пробелов, но вокруг сложения они поставлены. Такой прием позволяет визуально выделить приоритеты операторов, а в ряде случаев — избежать расстановки круглых скобок.

Используя вертикальное форматирование, мы выделяем объявление переменных, цикл заполнения массива случайными числами и цикл обработки по формуле. Если ваша функция выполняет несколько действий — то разумно разделить соответствующие блоки кода пустыми строками.

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

Имена

Коротко и ясно ключевое правило именования сформулировал Мартин — «имя должно отражать намерения программиста«, большинство других советов вытекает из этого утверждения: имя не должно дезинформировать, содержать лишнее, отвлекать, …. Достаточно часто пишут про недопустимость каламбуров и шуток в именах, а в моей педагогической деятельности был случай:

Студентка решала задачу о вычислении суммы ряда, при этом накопитель суммы назвала «i», а счетчики циклов — «s» и «o». На вопрос о том, почему она так назвала переменные, она ответила, что все ее одногруппники называют счетчики «i», а сумму — « или «sum» — поэтому это серые, унылые имена, а ей хочется писать красивые программы с гламурными именами.

Имена «i», «j» зарезервированы для счетчиков, от них не ожидают другого поведения. Имя «s» логично использовать для накопления суммы, а имя «o» — вообще лучше не использовать (визуально оно плохо отличимо от ноля).

Одно время мне приходилось использовать WinAPI, изобилующее всевозможными префиксами — разработчики Windows использовали Венгерскую нотацию, согласной которой тип своеобразным образом кодируется в имени [6].

В приведенном примере имена аргументов содержат закодированную информацию о типе. Так, префикс lp означает long pointer, префикс dwdouble word (два машинных слова — unsigned long), а префикс b кодирует логический тип данных.

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

Тем не менее, частично такие нотации до сих пор применяются — например, я нахожу удобным дополнять имя областью видимости. Соглашение о стиле кодирования Google [7] предлагает в конце имени данных-членов класса ставить символ подчеркивания. Согласно правилам кодирования mozilla [8] различные префиксы присваиваются данным-членам, глобальным переменным, аргументам функций, статическим членам и константам.

Мартин считает что и такие префиксы не нужны, его мнение разделяют многие программисты — на habrahabr не однократно проводились опросы о соглашениях кодирования и префиксах [9], которые это подтверждают. Например, вместо префикса, выделяющего данные-члены класса (часто это m, m_ или символ подчеркивания), предлагается использовать указатель на текущий объект — this (это не пройдет в списке инициализации конструктора):

Кроме префиксов областей видимости есть и другие, например:

  • имена абстрактных классов (интерфейсов) дополняются префиксом I (ISocket), напротив — классы реализации могут снабжаться постфиксом Imp (SocketImp) или Impl (при использовании идиомы Pimpl). Класс в составе иерархии может содержать префикс C (CFigure), а классы исключений — постфикс Exception (BadArgumentsException)[1, 2, 10]. Есть множество других вариантов, которые часто противоречат друг другу.
  • имена функций дополняются префиксами:
    • is_ — проверяет что-то и возвращает логический тип — is_digit;
    • has_ — выполняет поиск какого-либо значения в контейнере — has_primeNumber;
    • get_ и set_ — метод возвращает или устанавливает значение какого-либо поля — set_volume, set_volume;
  • используются схемы именования, позволяющие различать объекты и типы данных — имена пользовательских типов предлагают начинать с заглавной буквы, а имена объектов — со строчной.

Правила именования не ограничиваются префиксами, например:

  • названия должны использовать по возможности слова из предметной области решаемой задачи;
  • имена классов должны выражаться существительными, функций и методов — глаголами;
  • имена классов должны быть хорошо различимы — Страуструп приводит пример с именами fl, f1, fI и fi [11].

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

Приведен утрированный пример, однако, в файле pr.cpp окажутся как константы, объявленные первым программистом, так и вторым. Различия в обозначениях будут мешать обоим программистам — один может думать, что FIVE — это макрос, другой не сразу поймет, что TripleWaterPointTemp является константой.

Комментарии

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

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

От комментариев, несущих информацию об авторском праве и лицензии никуда не деться, однако с остальными можно бороться — в последнее время все шире распространяется мнение, о том, что «комментарии — признак плохого кода» [12, 13]. Впрочем, есть и другое мнение — так, например, соглашение о кодировании mozilla требует использовать комментарии в стиле JavaDoc [8], а Мейерс в одном из своих 55 советов упоминает doxygen [2].

Существуют разногласия, однако, все программисты сходятся на том, что:

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

Из этого понятно, что комментарий должен быть точным, коротким и по теме. Комментариев следует избегать — по возможности стоит переписать код так понятно, чтобы потребность в комментарии пропала вовсе. Использование современных инструментов разработки позволяют полностью исключить некоторые типы комментариев из программы:

  • информацию о версии программы, авторе изменений и ее особенностях позволяют хранить системы управления версиями [14 , 15];
  • комментарии TODO, BUG и FIXME могут быть перенесены в трекеры задач и ошибок.

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

Несмотря на то, что современные среды разработки умеют обрабатывать и выводить в окошках информацию о TODO, FIXME, NOTE и прочих специальных комментариях, могут существовать различия в формате:

Различные IDE могут как среагировать на оба комментария, так и ни на один из них :). В данном случае проблему создал один лишний пробел — в конторе где я работал с этим столкнулись, когда начали переносить проект с Windows (использовали Microsoft Visual Studio) на Linux (в качестве IDE выбрали Qt Creator).

Комментарии часто дублируют код — когда код понятен без комментариев. Особенно хорошо это видно при написании комментариев для doxygen или javadoc. Системы типа doxygen позволяют строить документацию к программе по исходному коду, при этом сама документация размазывается по коду в виде комментариев, записанных в специальном формате [16, 17]. Использования этих систем требуют многие соглашения о кодировании, использовались они и в фирме где я работал.

В приведенном фрагменте — «pure virtual member» заменяет описание функции, обычно на этом месте пишут что именно делает функция. Кроме того, часто пишут и короткое, и полное описание (они по-разному отображаются в документации). Тег «@see» позволяет связывать функции (связи отображаются в документации) — есть другие типы связей, а еще якоря, ссылки, секции, параграфы и т.п. Теги @param используются для описания аргументов функции.

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

В реальных программах недостатки таких комментариев еще более очевидны, т.к. в них нет непонятного «pure virtual member», а каждая функция решает вполне определенную проблему. Код, приведенный ниже, взят из статьи про разработку игры. Дописанные комментарии ничего по существу добавить не могут, однако значительно увеличивают размер исходного кода — очевидно, что на их написание и поддержку расходуется немало времени.

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

С другой стороны, чтобы использовать doxygen, совсем не обязательно писать дополнительные комментарии. Если в вашей программе используются нормальные имена, то и документация получится съедобной — система doxygen построит вам нужные диаграммы (не только такую, как приведена ниже) и упакует все в удобный для чтения формат:

диаграмма классов doxygen

Выводы этой статьи состоят в том, что:

  1. форматировать код надо. Единообразно;
  2. правильные имена переменных могут сильно помочь при поддержке проекта;
  3. комментарии не решают проблемы плохого кода;
  4. современные инструменты разработки могут значительно облегчить поддержку и улучшить код;
  5. не всем советам «чистого кода» надо слепо следовать — часть из них может навредить.

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

Кстати, вопросы именования переменных и некоторые связанные с этим проблемы, относящиеся именно к языку С++, а также конструктивную критику Венгерской нотации можно прочитать в отдельной статье: «Именование переменных и констант в С++» [19].

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

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

Роберту Мартину удалось идеально описать измерение качества кода кода:

Единственным ценным измерением качества кода является WTF/мин.

Объясню чуть подробнее. Когда я провожу code review, у меня бывает только три эмоции:

  1. WTF (с отвращением) — этот код не нужен.
  2. WTF (с восхищением) — этот человек умен.
  3. WTF (раздраженно) — эту ерунду невозможно разобрать.

Что же влияет на нас первым делом, когда мы видим любой код? Это чистота и красота его написания. Создание чистого и красивого кода — это знак отличного мастера.

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

Вот несколько способов, которые могут помочь вам в искусстве написания чистого и красивого кода.

Начните с имени

Кендрик Ламар отлично сказал:

Если я захочу рассказать реальную историю, то я начну со своего имени.

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

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

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

Функции должны делать одну вещь

Луис Салливан однажды сказал:

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

Существует только два золотых правила создания чистых функций:


  • Они должны быть небольшими
  • Они должны делать одну вещь и делать ее хорошо

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

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

Комментарии не исправят плохой код

Винус Уильямс заметила:

Каждый оставляет свои комментарии. Так рождаются слухи.

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

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

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

Форматирование кода — всегда приоритет

Форматирование кода — это коммуникация, а коммуникация — это приоритет для профессионального разработчика, — отмечает Роберт Мартин.

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

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

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

Сначала напишите try-catch-finally

Жорж Кангилем правильно сказал:

Ошибаться — это по-человечески, постоянно ошибаться — это бесчеловечно.

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

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

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

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

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

Заключение

Каким словом можно обобщить все сказанное здесь?

Это чувство кода, аналог здравого смысла в программном обеспечении.

Согласно Роберту Мартину, «написание чистого кода требует дисциплинированного использования мириад маленьких техник, примененных для ощущения чистоты. Эти маленькие методы вместе образуют чувство кода».

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

Подвести итог можно словами Гарольда Абельсона:

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

Принципы написания чистого CSS кода

Слишком часто крупные (да и мелкие) сайты страдают от слишком разросшегося, и по сути, избыточного CSS кода, где можно найти большое количество лишних элементов, слишком длинные и конкретизированные выражения, большую зависимость одних селекторов от других, частое и нецелесообразное использование !important и так далее. Для оптимизации наших таблиц стилей, мы конечно же можем воспользоваться какими-то базовыми основами написания CSS кода. Что мы действительно должны делать, так это добиваться хорошего структурирования таблиц стилей и стремиться к повышению устойчивости к высоким нагрузкам.

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

В этом уроке мы затронем эти темы, но перед этим давайте посмотрим на несколько примеров очистки CSS кода.

Несколько полезных методов

Частенько чей-то чужой опыт бывает очень полезным. Давайте рассмотрим несколько примеров:

Быстрый доступ к элементам

Очень важно иметь возможность получать быстрый доступ к какому-то стилю прямо со страницы. Для этого можно воспользоваться различными инструментами, такими как Explorer Developer Toolbar, Mozilla Firebug или Chrome Developer Tools. Используя данные инструменты, мы можем быстро находить то, что нам нужно, и тут же менять, видя результат.

В данном фрагменте присутствует свойство outline. Оно используется для одновременного присвоения цвета, стиля и толщины внешней границы на всех четырех сторонах элемента. Тут я выбрал слово red для определения цвета границы и заметьте, не просто так. Для определения конечных цветов всегда пользуйтесь rgb или hsl кодами, а для временных — цельными словами. Это поможет вам в будущем быстро найти нужные фрагменты и устранить спорные моменты, возникшие во время разработки. Будьте осторожны с использованием свойства outline, т.к. оно не работает в Internet Explorer 8.

Добавление тестовых стилей

Ещё одной хорошей практикой является добавление отступа для каких-то тестовых или временных свойств.

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

Отключение стилей

Часто при написании CSS кода нам нужно отключать какие-то стили. Мало кто знает, но для этого можно к свойству приписать префикс “x-”:

Этот способ быстрее, чем комментирование. Также это может упростить поиск таких спорных селекторов.

Очистка и оптимизация CSS кода

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

Макро-оптимизация

Прежде чем приступить к оптимизации таблиц стилей, нам нужно написать хорошо читабельный HTML и CSS код.

Формирование информации и определение структуры таблицы стилей

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

В первую очередь, я рекомендую вставлять какую-то информацию о пользователе (имя, …), время последней правки файла. Данный приём может сыграть свою роль, если в будущем возникнут какие-то вопросы, связанные с этими данными.

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

Знак “=” в последнем выражении используется исключительно для упрощения поиска отдельно взятой секции.

Аннотации и отображение вложенность

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

Аннотации формируются с помощью следующих выражений: или просто .

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

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

До

После

Микро-оптимизация

Микро-оптимизация направлена на уменьшение размера файла и увеличение скорости загрузки страницы.

Сортирование свойств в алфавитном порядке

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

Пример 1

Пример 2

Увеличение эффективности кода

Длинные селекторы заставляют браузер лишний раз проходиться по DOM-у и искать совпадения. Более конкретизированные названия селекторов позволяют избежать данную проблему.

Перед оптимизацией

После оптимизации

Пишите просто и кратко

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

  1. Используйте короткие названия CSS свойств везде, где это возможно;
  2. Пишите сокращённые значения свойств;
  3. Избегайте дублирования CSS свойств.

До

После

Сокращайте код

Главное, что вам нужно сделать на финальной стадии работы над стилями, — это убрать все дубликаты строк и различного рода отступы. Для рабочей версии стиля можно также убрать всевозможные комментарии. Также можно воспользоваться инструментами сжатия CSS стилей: CSS Compressor, CSS Drive.

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

Данный урок подготовлен для вас командой сайта ruseller.com
Источник урока: http://www.sitepoint.com/css-architectures-principles-of-code-cleanup-2/
Перевел: Станислав Протасевич
Урок создан: 25 Марта 2013
Просмотров: 39126
Правила перепечатки

5 последних уроков рубрики «CSS»

Забавные эффекты для букв

Небольшой эффект с интерактивной анимацией букв.

Реализация забавных подсказок

Небольшой концепт забавных подсказок, которые реализованы на SVG и anime.js. Помимо особого стиля в примере реализована анимация и трансформация графических объектов.

Анимированные буквы

Эксперимент: анимированные SVG буквы на базе библиотеки anime.js.

Солнцезащитные очки от первого лица

Прикольный эксперимент веб страницы отображение которой осуществляется “от первого лица” через солнцезащитные очки.

Раскрывающаяся навигация

Экспериментальный скрипт раскрывающейся навигации.

Написание эффективного кода

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

Код для первого скрина:

Код для второго скрина:

JS-код, предполагаемый объект

1 ответ 1

Вариант с .serializeArray() мне кажется будет не удобным при использовании, с использованием jQuery, можешь сделать что-то подобное:

Блог GunSmoker-а

. when altering one’s mind becomes as easy as programming a computer, what does it mean to be human.


16 января 2011 г.

Как писать понятный код — руководство для учащихся

aka «Как писать код, понятный хотя бы себе самому»

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

В результате студент обычно берёт в руки отвёртку и начинает заворачивать ей шурупы, забивать гвозди и сверлить дырки (угх!). При этом получается программа, которая содержит весь свой код в одной процедуре. Иногда эта единственная процедура может занимать десятки страниц. Добавьте сюда отсутствие форматирования, комментариев и вразумительных имён — и вы получите рождение нового монстра.

Этот пост — попытка рассказать, что можно сделать с такой ситуацией.

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

Вот улучшенный вариант кода: Одного взгляда на этот код достаточно, чтобы понять, что он имеет какое-то отношение к простым числам (prime numbers). Второй взгляд показывает, что он находит простые числа от 1 до MaxPrimes .

Что касается первого фрагмента, то, взглянув пару раз, вы даже не поймёте, где заканчиваются циклы!

Между этими кусками кода нет никаких различий — они выполняют одну и ту же задачу в точности одинаковым способом. Оба куска кода создают один и тот же машинный код. Но второй фрагмент кода читать намного проще. Добавьте в него ещё комментарии и вы получите почти идеальный код.

Форматирование кода

Первый самый важный (и относительно простой) момент — форматирование кода. От форматирования кода не зависят скорость выполнения, объём требуемой памяти и другие внешние аспекты программы. Но от форматирования кода зависит, насколько легко вы можете понять, пересмотреть и исправить код. А также, насколько легко поймёт этот код другой человек (скажем, преподаватель).

Посмотрите на этот код: Этот код синтаксически корректен. Он прокомментирован и содержит хорошие имена переменных и понятную логику. Если не верите, то попробуйте прочитать его. Это в точности второй блок кода этого поста, но с добавленными комментариями.

Чего этому коду не хватает — так это хорошего форматирования.

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

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

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

Самое простое, что можно сделать с форматированием — использовать инструмент автоматического форматирования кода. Например, в некоторых версиях Delphi такой инструмент уже есть. Вызывается он из меню Project / Format Project Sources :

Среда спросит вас, точно ли вы хотите отформатировать код в стандартный стиль оформления. Отвечайте «Yes» (Да) и весь код, подключенный в проект будет отформатирован в стандартном стиле.

Если вы используете Lazarus, то аналогичная команда находится в меню Service ( Сервис ):

А если вы используете PascalABC.NET, то аналогичная команда находится в меню Сервис (но только в автономной версии среды, а не online WDE):

Конечно же, прежде чем запускать эти команды, убедитесь, что программа синтаксически корректна — т.е. она компилируется. Скомпилируйте (или запустите) программу. Успешно прошло? Тогда можно запускать форматирование. Нет? Тогда сначала исправьте ошибки. (Я не говорю сейчас про ошибки во время работы программы, а только в момент компиляции).

Конечно, если вы делаете форматирование кода в надежде подсветить синтаксическую ошибку в вашем коде, то ни про какую успешную компиляцию речи идти не может. В этом случае просто запускайте форматирование. Найдите первую проблему. Исправьте его. Теперь повторите цикл заново (компилирование, форматирование, поиск ошибок).

Что делать, если в вашей версии Delphi такой команды нет? Вы можете воспользоваться программой JEDI Code Format (скачать) или DelForExp (скачать). Скачайте архив с программой (для DelForExp проще всего выбрать «Standalone version»). Распакуйте архив с программой. Теперь запускайте программу — файл JCFGui.exe для JEDI Code Format или DelFor.exe для DelForExp.

Для JEDI Code Format вам также понадобятся настройки стиля форматирования. Можете взять вот эти. Распакуйте этот архив в ту же папку, куда вы распаковали JEDI Code Formatter. Затем, укажите этот файл в настройках программы:

Теперь вы можете использовать команду File / Open (или соответствующую кнопку на панели инструментов), чтобы указать файл для форматирования:

Вы можете также установить опцию «No backup», как я сделал это на снимке экрана выше — такая настройка переформатирует файл «на месте».

Теперь достаточно нажать кнопку с зелёной стрелочкой и файл будет переформатирован.

Что касается DelForExp, то в нём всё то же самое: File / Open , указали файл, нажали на кнопку форматирования (только там нарисована молния, а не стрелочка, как в JEDI Code Format) и сохранили результат:

К сожалению, все описываемые способы имеют разные возможности. Кто-то выполняет очень мало действий и имеет мало настроек (или не имеет их вовсе), кто-то позволяет довольно много всего. Наиболее функциональными вариантами видятся JEDI Code Format и форматтер в Delphi. Наименее функциональными — встроенные варианты в Lazarus и PascalABC.NET.

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

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

Комментирование кода

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

К примеру, что делает этот код? Этот метод вычисляет первые num чисел Фибоначчи. Стиль кодирования этого метода чуть лучше, чем у самого первого нашего примера, но комментарий, описывающий код, неверен. Это сводит на нет всю пользу от комментирования кода. Если вы поверите этому комментарию, то будете введены в заблуждение.

А что скажете насчёт такого кода? Этот метод возводит целое число base в целую степень num. Комментарии в этом коде верны, но они не говорят о коде ничего нового. Это не более чем многословная версия самого кода. Цикл от 2 до «num»? Я и так вижу, что это цикл от 2 до num — зачем это повторять? Это только создаёт лишний шум (мусор).

Наконец, ещё один код: Код вычисляет квадратный корень из num. Код не идеален, но комментарий верен и комментирует цель кода, а не дублирует код.

Какой метод было проще всего понять? Все они написаны довольно плохо — особенно неудачны имена переменных. Эти куски кода иллюстрируют достоинства и недостатки комментариев. Комментарий первого кода неверен. Комментарии второго кода просто дублируют код и потому бесполезны. Только комментарии третьего кода оправдывают своё существование.

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

Вот несколько правил, придерживаясь которых, вы сделаете свою программу понятнее:

  • Не пишите комментарии, которые повторяют код.
  • Пишите комментарии на уровне цели кода. Комментарий должен отвечать на вопрос «зачем?» или «почему?», а не «как?» или «что?». Что и как — видно из кода. А комментарий должен говорить о том, что код не говорит — а зачем мы вообще это делаем? К примеру: А вот пример полезного комментария, отвечающего на вопрос «почему?»: Этот комментарий гораздо лучше, потому что говорит что-то, чего нет в коде.
  • Рассмотрите возможность замены комментария улучшением кода. К примеру, предыдущий пример можно было переписать так: или так: В обоих случаях код становится настолько очевиден, что комментарий уже не нужен. (дальнейшее улучшение: переименовать AccountFlag в AccountType и сделать её не числом, а перечислимым типом.)
  • Большинство полезных комментариев в программе состоят из одной-двух строк и комментируют блок кода за ними, например:
  • Избегайте комментирования отдельных строк кода. Если отдельная строка требует комментирования — это признак, что её надо переписать. Сюда же относятся комментарии в конце строк. Да, иногда бывают и исключения, но обычно польза таких комментариев сомнительна. Хороший пример полезного использования комментарии в конце строк — пояснение цели переменной при её объявлении.
  • Размещайте комментарии на отдельных строках.
  • Используйте для однострочных комментариев и для многострочных.
  • Придерживайтесь одного стиля комментирования. К примеру, вставляйте поясняющий комментарий до блока кода, а не после. Не отделяйте комментарий пустыми строками от блока кода, к которому он относится. Но вставьте по пустой строке до и после всего блока с комментарием, чтобы отделить их от других аналогичных блоков.
  • Не украшайте комментарии сверх меры. Это затрудняет чтение и их модификацию. Если вы тратите своё время на исправлениие оформления и выравнивания комментариев или стиля кода после того, как вы переименовали переменную, то вы не программируете — вы занимаетесь ерундой. Используйте такой стиль, который не потребует переформатирования при правках. Вот пример неудачного стиля: Если длина комментария меняется, вам нужно выравнивать оформление.
  • Избегайте сокрашений. Цель комментария — пояснить код. Использование сокрашений не помогает достижению этой цели. Не нужно заставлять читающих расшифровывать обозначения.

Кодирование

Программирование с псевдокодом

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

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

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

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

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

Например, процесс кодирования одной задачи мог бы выглядеть примерно так:

  • Определите решаемую задачу. Сформулируйте задачу, которую будете решать. Определите входные данные (что вводим), выходные данные (результаты, что получаем), обязательно соблюдаемые условия (к примеру, какой-то параметр должен быть больше нуля и т.д.), что метод должен скрывать, а что — показывать.
  • Исследуйте существующую функциональность. Посмотрите, быть может эту задачу решает какая-то стандартная функция языка, либо какой-то другой, уже написанный вами, метод.
  • Выберите название метода или функции, выполняющей задачу. Вопрос выбора хорошего названия кажется тривиальным, но дело это непростое. Затруднение в выборе имени может свидетельствовать, что задача не понятна, либо вы пытаетесь делать несколько вещей в одном месте.
  • Продумайте обработку ошибок. Подумайте о всём плохом, что может случится. Что если кто-то передал вам -1 в параметре, который должен быть больше 0? Что если пользователь ввёл не число? Что если файл уже существует? Продумайте, как вы будете реагировать на эти ситуации.
  • Продумайте типы данных, с которыми вы собираетесь работать.
  • Исследуйте алгоритмы и типы данных. Вы можете взять готовый алгоритм и адаптировать его к своей задаче.
  • Напишите псевдокод. Если вы прошли предыдущие этапы, то это не должно составить сложности. Вы можете писать псевдокод прямо в редакторе кода Delphi. Начните с основных моментов, с самого верхнего уроня, а затем детализируйте их.
  • Вы можете написать несколько вариантов псевдокода и выбрать лучший.
  • Сделайте псевдокод комментариями и закодируйте его на языке программирования.

Давайте посмотрим, как это работает на примере. Пусть перед нами стоит задача «найти все простые числа до заданного пользователем». Вот как бы могли её решать:

  • Итак, сначала мы определяем условия задачи. Нужно написать программу, которая будет искать простые числа. Максимальный предел вводится пользователем, так что нам понадобится ввести одно число. В результате мы получим список чисел — нам их надо куда-то вывести. По определению, Простые числа больше нуля и 1 — не простое число. Так что разумно наложить ограничение, что входной параметр должен быть целым числом, большим единицы. Выходными данными будет массив целых чисел, представляющих собой простые числа.

    Уже в этот момент можно запустить Delphi, создать новое VCL приложение и бросить на форму Edit (для ввода данных), Memo (для вывода данных) и Button (для запуска поиска).

    Выбор названий. Ну, давайте назовём Edit на форме — edMaxPrime , Memo — mmPrimes , а кнопку — btCalculatePrimes . Здесь же можно быстренько сделать косметические изменения — типа ReadOnly для Memo и так далее.

    Далее надо написать интерфейс метода, который будет выполнять поставленную задачу. В данном случае название метода достаточно очевидно — скажем, CalculatePrimes . Как мы определили в анализе предварительных требований, ему на вход нужно число — максимальное простое число для поиска, а нам он должен вернуть массив простых чисел. Запишем это так: Добавим заголовочный комментарий поясняющий метод, его требования, а заодно и определённый нами пользовательский тип: Как видите, уже здесь в код включается важная информация — условие для AMaxPrimes и описание поведение при краевом случае (включать ли в результат само число AMaxPrimes или нет).

    Далее, надо бы выбрать или придумать алгоритм, которым мы будем решать эту задачу. Иногда, этот шаг фиксируется преподавателем, иногда он остаётся на ваш выбор. В данном случае мы выберем решето Эратосфена (ну, это будет не в точности этот алгоритм, но очень похож).

    Будем последовательно записывать алгоритм на псевдокоде: Затем: (Здесь единственный комментарий в первом варианте является, по сути, заголовочным, поэтому мы вынесем его в описание функции)

    Достаточно просто. Вы можете легко увидеть, что должна делать программа и проверить правильность её действий.

    Мы создали достаточно подробный псевдокод всего в два шага. Если бы алгоритм был бы сложнее, нам бы могло потребоваться несколько проходов с всё более подробным описанием деталей. Хотя, возможно, последний шаг можно было бы расписать подробнее. Но пока остановимся на этом.

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

    Продолжаем кодировать: И далее: И так далее. В итоге вы получите готовый код.

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

    Этот процесс может быть рекурсивным. Т.е. код в выделенных подпрограммах тоже может потребовть дальнейшего разбиения на подпрограммы. К примеру, код SetResult может быть таким: Вы можете захотеть выделить в ней такую подпрограмму:

    Удаление ненужных комментариев. Некоторые получаемые комментарии могут быть избыточны и могут быть удалены. К примеру, «Для каждого числа от 2 до AMaxPrimes» может быть рассмотрен как избыточный (поскольку он дублирует информацию цикла), но, с другой стороны, он является частью следующего комментария. Лучшим решением будет объединить оба комментария. Не нужным является и «Подсчитать количество не вычеркнутых чисел». В итоге, подчистив всё, мы получаем такой код:

    Обработка ошибок. Для примера я вынес этот момент в самый конец, но по плану он должен стоять ближе к началу. К примеру, в нашем случае это условие для AMaxPrimes . Суть в том, что в методе должны стоять проверки таких ограничений. Делать это можно разными способами, я не буду рассматривать тут варианты, поскольку это выходит за рамки этой заметки. Укажу лишь способ, которой предпочитаю лично я: в самом методе все его ограничения заворачиваются в Assert, вот так: Что касается необходимости получать «читабельные» сообщения об ошибках, то делать это может вызывающий. Пример этого рассмотрен чуть ниже.

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

    Дальше надо. скомпилировать программу :) Да, до этого момента мы ещё ни разу не запускали её. Здесь нужно убедиться, что программа компилируется. Также нужно устранить все замечания компилятора по коду.

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

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

    Другие темы кодирования

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

    Самое минимальное, что тут можно сказать — давайте названия компонентам, которые вы бросаете на форму! Не оставляйте им названия вроде Button1 , Edit1 , Edit2 и т.д. И снова пример (это реальный пример кода с форума): Что делает этот код? Я понятия не имею. И никто этого не знает, за исключением самого автора кода. Но если вы назовёте компоненты, то код станет понятным: Тут уже стало понятно, что речь идёт про сохранение редактируемой записи в файл. И ещё небольшие улучшения дадут нам:
    Тем не менее, из примеров выше у вас должно было уже сложиться какое-то минимальное представление о правильном коде. А, для надёжности, я привожу краткую выдержку в заключении.

    Заключение

    Я надеюсь, что следуя этим советам, вы сможете писать понятный код. Для удобства я приведу контрольный список вещей для выполнения:

    • Форматирование кода:
      • Делали ли вы автоматическое форматирование кода?
      • Применяется ли форматирование кода для логического структурирования кода?
      • Однообразно ли форматирование?
      • Улучшает ли стиль форматирование читаемость кода?
      • Отделяются ли последовательные блоки друг от друга пустыми строками?
      • Форматируются ли сложные выражения для повышения удобочитаемости?
      • Не содержит ли какая-то строка более одного оператора?
      • Сделаны ли для комментариев такие же отступы, как и для кода?
      • Отформатированы ли прототипы функций и методов так, чтобы их было легко читать?
      • Используются ли пустые строки для разделения составных частей функций и методов?
      • Применяете ли вы схему хранения и именования множества функций, методов и классов?
    • Комментирование кода:
      • Использовали ли вы прототипирование с псевдокодом?
      • Может ли сторонний человек, взглянув на код, понять его?
      • Объясняют ли комментарии цель кода?
      • Переписали ли вы непонятный код, вместо того, чтобы комментировать его?
      • Актуальны ли и правдивы ли комментарии?
      • Позволяет ли стиль комментариев быстро их менять?
    • Стиль кода:
      • Присвоены ли переменным, типам и функциям удачные имена?
      • Выполняют ли функции и методы лишь одно действие?
      • Имеют ли ваши функции и методы небольшой размер?
      • Вынесен ли дублирующийся код в отдельные функции или методы?
      • Очевиден ли и ясен интерфейс каждой функции, метода и класса?
      • Используются ли переменные лишь один раз, а не повторно?
      • Используются ли переменные с той целью, с которой они названы?
      • Используются ли перечислимые типы вместо целочисленных типов с константами или логических флажков?
      • Используте ли вы волшебные константы?
      • Используете ли вы дополнительные переменные для пояснения кода?
      • Просты ли ваши типы данных? Помогают ли они понять программу?
      • Очевиден ли стандартный путь выполнения программы?
      • Сгруппированы ли связанные операторы?
      • Вынесен ли независимый код в отдельные функции или методы?
      • Просты ли управляющие структуры?
      • Выполняют ли циклы только одну функцию?
      • Пишете ли вы программу в терминах проблемной области?

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

    Написание безопасного и эффективного кода C# Write safe and efficient C# code

    Новые возможности в C# позволяют создавать проверяемый безопасный код с более высокой производительностью. New features in C# enable you to write verifiable safe code with better performance. Если вы будете внимательно применять эти методы, у вас будет меньше сценариев, требующих небезопасного кода. If you carefully apply these techniques, fewer scenarios require unsafe code. Эти функции упрощают использование ссылок на типы значений в качестве аргументов метода и возвращаемых значений метода. These features make it easier to use references to value types as method arguments and method returns. При безопасном выполнении эти методики сводят к минимуму копирование типов значений. When done safely, these techniques minimize copying value types. Используя типы значений, можно свести к минимуму число распределений и сборок мусора. By using value types, you can minimize the number of allocations and garbage collection passes.

    В большей части примера кода в этой статье демонстрируются функции, добавленные в C# 7.2. Much of the sample code in this article uses features added in C# 7.2. Чтобы их использовать, нужно настроить проект для работы с языком C# 7.2 или более поздней версии. To use those features, you must configure your project to use C# 7.2 or later. Дополнительные сведения об установке версии языка см. в разделе Настройка языковой версии. For more information on setting the language version, see configure the language version.

    Эта статья посвящена методам эффективного управления ресурсами. This article focuses on techniques for efficient resource management. Преимущество использования типов значений заключается в том, что они часто позволяют избежать выделения памяти в кучах. One advantage to using value types is that they often avoid heap allocations. Недостаток состоит в том, что они копируются по значению. The disadvantage is that they’re copied by value. Этот компромисс усложняет оптимизацию алгоритмов, работающих с большими объемами данных. This tradeoff makes it harder to optimize algorithms that operate on large amounts of data. Новые возможности языка в C# 7.2 предоставляют механизмы, обеспечивающие безопасный и эффективный код со ссылками на типы значений. New language features in C# 7.2 provide mechanisms that enable safe efficient code using references to value types. При рациональном использовании этих функций можно свести к минимуму число операций выделения и копирования. Use these features wisely to minimize both allocations and copy operations. В этой статье приводится описание этих новых функций. This article explores those new features.

    Эта статья описывает следующие методы управления ресурсами: This article focuses on the following resource management techniques:

    • Объявите readonly struct , чтобы обозначить, что тип является неизменяемым и позволяет компилятору сохранять копии при использовании параметров in . Declare a readonly struct to express that a type is immutable and enables the compiler to save copies when using in parameters.
    • Если тип не может быть неизменяемым, объявите члены struct readonly , чтобы указать, что элемент не изменяет состояние. If a type can’t be immutable, declare struct members readonly to indicate that the member doesn’t modify state.
    • Используйте ref readonly , если возвращаемое значение — struct больше, чем IntPtr.Size, и время существования хранилища больше, чем метод, возвращающий значение. Use a ref readonly return when the return value is a struct larger than IntPtr.Size and the storage lifetime is greater than the method returning the value.
    • Если размер readonly struct больше, чем IntPtr.Size, необходимо передать его как параметр in для повышения производительности. When the size of a readonly struct is bigger than IntPtr.Size, you should pass it as an in parameter for performance reasons.
    • Никогда не передавайте struct как параметр in , если он не объявлен с модификатором readonly или метод вызывает только члены readonly структуры. Never pass a struct as an in parameter unless it’s declared with the readonly modifier or the method calls only readonly members of the struct. Нарушение этого правила может негативно сказаться на производительности и привести к непонятному поведению. Violating this guidance may negatively affect performance and could lead to an obscure behavior.
    • Используйте ref struct или readonly ref struct , например Span или ReadOnlySpan , для работы с памятью как последовательностью байтов. Use a ref struct , or a readonly ref struct such as Span or ReadOnlySpan to work with memory as a sequence of bytes.

    Эти методы помогают найти баланс между двумя противоположными целями в отношении ссылок и значений. These techniques force you to balance two competing goals with regard to references and values. Переменные, которые являются ссылочными типами, содержат ссылку на расположение в памяти. Variables that are reference types hold a reference to the location in memory. Переменные, которые являются типами значений, непосредственно содержат значение. Variables that are value types directly contain their value. Это ключевые различия, которые важны для управления ресурсами памяти. These differences highlight the key differences that are important for managing memory resources. Типы значений обычно копируются при передаче в метод или возвращаются из метода. Value types are typically copied when passed to a method or returned from a method. Это поведение включает в себя копирование значения this при вызове членов типа значения. This behavior includes copying the value of this when calling members of a value type. Издержки копирования связаны с размером типа. The cost of the copy is related to the size of the type. Ссылочные типы размещаются в управляемой куче. Reference types are allocated on the managed heap. Каждый новый объект требует новое распределение и впоследствии должен быть освобожден. Each new object requires a new allocation, and subsequently must be reclaimed. Обе эти операции занимают время. Both these operations take time. Ссылка копируется, когда ссылочный тип передается в качестве аргумента в метод или возвращается из метода. The reference is copied when a reference type is passed as an argument to a method or returned from a method.

    В этой статье используется следующий пример концепции трехмерной структуры для объяснения этих рекомендаций: This article uses the following example concept of the 3D-point structure to explain these recommendations:

    Различные примеры использования других реализаций этой концепции. Different examples use different implementations of this concept.

    Объявление структур только для чтения для неизменяемых типов значений Declare readonly structs for immutable value types

    Объявление struct с помощью модификатора readonly сообщает компилятору, что ваша цель — создать неизменяемый тип. Declaring a struct using the readonly modifier informs the compiler that your intent is to create an immutable type. Компилятор указывает это решение со следующими правилами: The compiler enforces that design decision with the following rules:

    • Все члены поля должны быть readonly All field members must be readonly
    • Все свойства должны быть только для чтения, включая автоматически реализуемые свойства. All properties must be read-only, including auto-implemented properties.

    Этих двух правил достаточно, чтобы убедиться, что ни один из элементов readonly struct не изменяет состояние этой структуры. These two rules are sufficient to ensure that no member of a readonly struct modifies the state of that struct. Объект struct является неизменяемым. The struct is immutable. Структура Point3D может быть определена как неизменяемая, как показано в следующем примере: The Point3D structure could be defined as an immutable struct as shown in the following example:

    Следуйте этим рекомендациям, когда планируете создать неизменяемый тип значения. Follow this recommendation whenever your design intent is to create an immutable value type. Улучшения производительности являются дополнительным преимуществом. Any performance improvements are an added benefit. readonly struct четко выражает ваше намерение. The readonly struct clearly expresses your design intent.

    Объявление членов, доступных только для чтения, если структура не может быть неизменяемой Declare readonly members when a struct can’t be immutable

    В C# 8.0 и более поздних версиях, когда тип структуры является изменяемым, следует объявить члены, которые не приводят к тому, что изменение имеет статус readonly . In C# 8.0 and later, when a struct type is mutable, you should declare members that don’t cause mutation to be readonly . Например, ниже приведена изменяемая разновидность трехмерной структуры точек: For example, the following is a mutable variation of the 3D point structure:

    В предыдущем примере показаны многие расположения, в которых можно применить модификатор readonly : методы, свойства и методы доступа к свойствам. The preceding sample shows many of the locations where you can apply the readonly modifier: methods, properties, and property accessors. При использовании автоматически реализуемых свойств компилятор добавляет модификатор readonly к методу доступа get для свойств, предназначенных для чтения и записи. If you use auto-implemented properties, the compiler adds the readonly modifier to the get accessor for read-write properties. Компилятор добавляет модификатор readonly к автоматически реализуемым объявлениям свойств для свойств только с методом доступа get . The compiler adds the readonly modifier to the auto-implemented property declarations for properties with only a get accessor.

    Добавление модификатора readonly к членам, которые не изменяют состояние, предоставляет два связанных преимущества. Adding the readonly modifier to members that don’t mutate state provides two related benefits. Во первых, компилятор применяет свое намерение. First, the compiler enforces your intent. Этот член не может изменять состояние структуры и получить доступ к члену, который также не помечен как readonly . That member can’t mutate the struct’s state, nor can it access a member that isn’t also marked readonly . Во-вторых, компилятор не создает защитные копии параметров in при доступе к члену readonly . Second, the compiler won’t create defensive copies of in parameters when accessing a readonly member. Компилятор может безопасно выполнить эту оптимизацию, так как она гарантирует, что struct не изменяется членом readonly . The compiler can make this optimization safely because it guarantees that the struct is not modified by a readonly member.

    Используйте операторы ref readonly return для больших структур, когда это возможно Use ref readonly return statements for large structures when possible

    Вы можете возвращать значения по ссылке, когда возвращаемое значение не является локальным для возвращающего метода. You can return values by reference when the value being returned isn’t local to the returning method. Возврат по ссылке означает, что копируется только ссылка, не структура. Returning by reference means that only the reference is copied, not the structure. В следующем примере свойство Origin не может использовать возврат ref , так как возвращаемое значение является локальной переменной: In the following example, the Origin property can’t use a ref return because the value being returned is a local variable:

    Тем не менее, следующее определение свойства может возвращаться по ссылке, так как возвращаемое значение является статическим элементом: However, the following property definition can be returned by reference because the returned value is a static member:

    Вы не хотите, чтобы вызывающие объекты изменяли источник, поэтому следует возвращать значение через readonly ref : You don’t want callers modifying the origin, so you should return the value by readonly ref :

    Возвращение ref readonly позволяет сохранить копирование больших структур и неизменность внутренних элементов данных. Returning ref readonly enables you to save copying larger structures and preserve the immutability of your internal data members.

    Во время вызова вызывающие объекты выбирают использовать свойство Origin как readonly ref или как значение: At the call site, callers make the choice to use the Origin property as a readonly ref or as a value:

    При первом назначении в предыдущем примере кода создается и назначается копия константы Origin . The first assignment in the preceding code makes a copy of the Origin constant and assigns that copy. При втором назначается ссылка. The second assigns a reference. Обратите внимание, что модификатор readonly должен быть частью объявления переменной. Notice that the readonly modifier must be part of the declaration of the variable. Ссылку, на которую он ссылается, изменить невозможно. The reference to which it refers can’t be modified. В противном случае возникнет ошибка времени компиляции. Attempts to do so result in a compile-time error.

    В объявлении originReference требуется модификатор readonly . The readonly modifier is required on the declaration of originReference .

    Компилятор применяет правило, не позволяющее вызывающему объекту изменять ссылку. The compiler enforces that the caller can’t modify the reference. Попытки назначить значение напрямую вызывают ошибку времени компиляции. Attempts to assign the value directly generate a compile-time error. Однако компилятору не может быть известно, изменяет ли какой-либо метод члена состояние структуры. However, the compiler can’t know if any member method modifies the state of the struct. Чтобы защитить объект от изменений, компилятор создает копию и с ее помощью вызывает ссылки на члены. To ensure that the object isn’t modified, the compiler creates a copy and calls member references using that copy. Все изменения будут вноситься в эту защитную копию. Any modifications are to that defensive copy.

    Применение модификатора in к параметрам readonly struct , больше чем System.IntPtr.Size Apply the in modifier to readonly struct parameters larger than System.IntPtr.Size

    При передаче аргументов по ссылке можно использовать ключевое слово in как дополнение к существующим ключевым словам ref и out . The in keyword complements the existing ref and out keywords to pass arguments by reference. Ключевое слово in указывает, что аргумент передается по ссылке, но вызванный метод не изменяет это значение. The in keyword specifies passing the argument by reference, but the called method doesn’t modify the value.

    Это дополнение позволяет полностью выразить намерение разработки. This addition provides a full vocabulary to express your design intent. Если в сигнатуре метода не указан ни один из следующих модификаторов, типы значений копируются при передаче в вызываемый метод. Value types are copied when passed to a called method when you don’t specify any of the following modifiers in the method signature. Каждый из этих модификаторов указывает, что переменная передается по ссылке, предотвращая копирование. Each of these modifiers specifies that a variable is passed by reference, avoiding the copy. Каждый модификатор выражает конкретное намерение. Each modifier expresses a different intent:

    • out . этот метод задает значение аргумента, используемого в качестве этого параметра. out : This method sets the value of the argument used as this parameter.
    • ref . этот метод может задавать значение аргумента, используемого в качестве этого параметра. ref : This method may set the value of the argument used as this parameter.
    • in . этот метод не изменяет значение аргумента, используемого в качестве этого параметра. in : This method doesn’t modify the value of the argument used as this parameter.

    При добавлении модификатора in для передачи аргумента по ссылке вы объявляете о своем намерении передавать аргументы по ссылке, чтобы избежать ненужных операций копирования. Add the in modifier to pass an argument by reference and declare your design intent to pass arguments by reference to avoid unnecessary copying. Вы не собираетесь изменять объект, используемый в качестве этого аргумента. You don’t intend to modify the object used as that argument.

    Такой подход часто повышает производительность для типов значений только для чтения, размер которых превышает IntPtr.Size. This practice often improves performance for readonly value types that are larger than IntPtr.Size. Для простых типов ( sbyte , byte , short , ushort , int , uint , long , ulong , char , float , double , decimal и bool и enum ) возможное повышение производительности минимально. For simple types ( sbyte , byte , short , ushort , int , uint , long , ulong , char , float , double , decimal and bool , and enum types), any potential performance gains are minimal. На самом деле, производительность может снизиться при использовании передачи по ссылке для типов, меньше чем IntPtr.Size. In fact, performance may degrade by using pass-by-reference for types smaller than IntPtr.Size.

    Ниже приведен пример метода, который вычисляет расстояние между двумя точками в трехмерном пространстве. The following code shows an example of a method that calculates the distance between two points in 3D space.

    Аргументами являются две структуры, каждая из которых содержит три типа double. The arguments are two structures that each contain three doubles. double имеет размер 8 байт, поэтому каждый аргумент равен 24 байтам. A double is 8 bytes, so each argument is 24 bytes. Указывая модификатор in , вы, в зависимости от архитектуры компьютера, передаете этим аргументам 4- или 8-байтовую ссылку. By specifying the in modifier, you pass a 4 byte or 8-byte reference to those arguments, depending on the architecture of the machine. Разница в размере невелика, но она может вырасти, когда приложение вызывает этот метод в непрерывном цикле с помощью множества различных значений. The difference in size is small, but it adds up when your application calls this method in a tight loop using many different values.

    Существуют и другие способы, которыми модификатор in дополняет out и ref . The in modifier complements out and ref in other ways as well. Невозможно создать перегрузки метода, которые отличаются только наличием in , out или ref . You can’t create overloads of a method that differ only in the presence of in , out , or ref . Эти новые правила расширяют то же поведение, которое всегда действовало для параметров out и ref . These new rules extend the same behavior that had always been defined for out and ref parameters. Как и модификаторы out и ref , типы значений не упаковываются, так как применяется модификатор in . Like the out and ref modifiers, value types aren’t boxed because the in modifier is applied.

    Модификатор in может применяться к любому члену, который принимает параметры: к методам, делегатам, лямбда-выражениям, локальным функциям, индексаторам, операторам. The in modifier may be applied to any member that takes parameters: methods, delegates, lambdas, local functions, indexers, operators.

    Еще одно преимущество параметров in состоит в том, что вы можете использовать литеральные значения или константы для аргумента в параметре in . Another feature of in parameters is that you may use literal values or constants for the argument to an in parameter. Кроме того, в отличие от параметра ref или out , не нужно применять модификатор in на сайте вызова. Also, unlike a ref or out parameter, you don’t need to apply the in modifier at the call site. В следующем коде показаны два примера вызова метода CalculateDistance . The following code shows you two examples of calling the CalculateDistance method. В первом используются две локальные переменные, передаваемые по ссылке. The first uses two local variables passed by reference. Второй содержит временную переменную, созданную в рамках вызова метода. The second includes a temporary variable created as part of the method call.

    Существует несколько способов, когда компилятор гарантирует, что аргумент in доступен только для чтения. There are several ways in which the compiler enforces the read-only nature of an in argument. Во-первых, вызванный метод не может быть напрямую назначен параметру in . First of all, the called method can’t directly assign to an in parameter. Его невозможно напрямую назначить полю параметра in , когда это значение имеет тип struct . It can’t directly assign to any field of an in parameter when that value is a struct type. Кроме того, параметр in невозможно передать какому-либо методу, использующему модификатор ref или out . In addition, you can’t pass an in parameter to any method using the ref or out modifier. Эти правила применяются к любому полю параметра in при условии, что данное поле имеет тип struct и параметр имеет тип struct . These rules apply to any field of an in parameter, provided the field is a struct type and the parameter is also a struct type. На самом деле эти правила применяются к нескольким уровням доступа к членам при условии, что все уровни доступа к членам являются structs . In fact, these rules apply for multiple layers of member access provided the types at all levels of member access are structs . Компилятор принудительно указывает, что типы struct , передаваемые в качестве аргументов in , и их члены struct являются переменными, доступными только для чтения, когда используются в качестве аргументов для других методов. The compiler enforces that struct types passed as in arguments and their struct members are read-only variables when used as arguments to other methods.

    Использование параметров in позволяет избежать потенциальных затрат на создание копий. The use of in parameters can avoid the potential performance costs of making copies. Это не меняет семантику ни одного вызова метода. It doesn’t change the semantics of any method call. Таким образом, указывать модификатор in в месте вызова не нужно. Therefore, you don’t need to specify the in modifier at the call site. Пропуск модификатора in в месте вызова сообщает компилятору, что он может сделать копию аргумента по следующим причинам: Omitting the in modifier at the call site informs the compiler that it’s allowed to make a copy of the argument for the following reasons:

    • Выполняется неявное преобразование, но не преобразование удостоверения из типа аргумента в тип параметра. There exists an implicit conversion but not an identity conversion from the argument type to the parameter type.
    • Аргумент является выражением, но не имеет известную переменную хранения. The argument is an expression but doesn’t have a known storage variable.
    • Существует перегрузка, которая отличается наличием или отсутствием in . An overload exists that differs by the presence or absence of in . В этом случае перегрузка по значению подходит лучше. In that case, the by value overload is a better match.

    Эти правила полезны и при обновлении существующего кода для использования аргументов со ссылками, доступными только для чтения. These rules are useful as you update existing code to use read-only reference arguments. Внутри вызываемого метода можно вызвать любой метод экземпляра, который использует параметры передачи по значению. Inside the called method, you can call any instance method that uses by value parameters. В этих экземплярах создается копия параметра in . In those instances, a copy of the in parameter is created. Поскольку компилятор может создавать временную переменную для любого параметра in , вы можете также указать значения по умолчанию для любого параметра in . Because the compiler may create a temporary variable for any in parameter, you can also specify default values for any in parameter. Следующий код указывает начало координат (точку 0,0) в качестве значения по умолчанию для второй точки: The following code specifies the origin (point 0,0) as the default value for the second point:


    Чтобы велеть компилятору передавать аргументы, доступные только для чтения, по ссылке, укажите модификатор in для аргументов в месте вызова, как показано в следующем коде: To force the compiler to pass read-only arguments by reference, specify the in modifier on the arguments at the call site, as shown in the following code:

    Это упрощает постепенное внедрение параметров in в больших базах кода, где возможен выигрыш по производительности. This behavior makes it easier to adopt in parameters over time in large codebases where performance gains are possible. Сначала нужно добавить модификатор in в сигнатуры методов. You add the in modifier to method signatures first. Затем можно добавить модификатор in в местах вызовов и создать типы readonly struct , чтобы разрешить компилятору не создавать защитные копии параметров in в дополнительных расположениях. Then, you can add the in modifier at call sites and create readonly struct types to enable the compiler to avoid creating defensive copies of in parameters in more locations.

    Обозначение параметра in также можно использовать со ссылочными типами или числовыми значениями. The in parameter designation can also be used with reference types or numeric values. Однако преимущества в обоих случаях минимальны (если они вообще есть). However, the benefits in both cases are minimal, if any.

    Никогда не используйте изменяемые структуры, как в аргументе in Never use mutable structs as in in argument

    Методики, описанные выше, объясняют, как избежать копий путем возврата ссылок и передачи значения по ссылке. The techniques described above explain how to avoid copies by returning references and passing values by reference. Эти методы лучше всего работают, когда типы аргументов объявляются как типы readonly struct . These techniques work best when the argument types are declared as readonly struct types. В противном случае компилятор должен создавать защитные копии во многих ситуациях, чтобы гарантировать, что все аргументы будут доступны только для чтения. Otherwise, the compiler must create defensive copies in many situations to enforce the readonly-ness of any arguments. Рассмотрим следующий пример, который вычисляет расстояние до точки в трехмерном пространстве от начала координат: Consider the following example that calculates the distance of a 3D point from the origin:

    Структура Point3D не предоставляется только для чтения. The Point3D structure is not a readonly struct. В тексте этого метода есть шесть разных вызовов доступа к свойству. There are six different property access calls in the body of this method. На первый взгляд может показаться, что эти доступы безопасны. On first examination, you may have thought these accesses were safe. В конце концов, метод доступа get не должен изменять состояние объекта. After all, a get accessor shouldn’t modify the state of the object. Но нет правила языка, которое это обеспечивает. But there’s no language rule that enforces that. Это просто общее соглашение. It’s only a common convention. Любой тип может реализовывать метод доступа get , который изменил внутреннее состояние. Any type could implement a get accessor that modified the internal state. Без языковой гарантии компилятору необходимо создавать временную копию аргумента перед вызовом любого члена. Without some language guarantee, the compiler must create a temporary copy of the argument before calling any member. Временное хранилище создается на стеке, значения аргумента копируются во временном хранилище, и значение копируется в стек для каждого доступа к членам как аргумент this . The temporary storage is created on the stack, the values of the argument are copied to the temporary storage, and the value is copied to the stack for each member access as the this argument. Во многих случаях эти копии достаточно снижают производительность, так что передача по значению работает быстрее, чем передача по ссылке только для чтения, если тип аргумента не readonly struct . In many situations, these copies harm performance enough that pass-by-value is faster than pass-by-readonly-reference when the argument type isn’t a readonly struct .

    Вместо этого, если вычисление расстояния использует неизменяемую структуру, ReadonlyPoint3D , временные объекты не требуются: Instead, if the distance calculation uses the immutable struct, ReadonlyPoint3D , temporary objects aren’t needed:

    Компилятор создает более эффективный код, когда вызываются члены readonly struct : Ссылка this , а не копия приемника, всегда является параметром in , переданным по ссылке методу члена. The compiler generates more efficient code when you call members of a readonly struct : The this reference, instead of a copy of the receiver, is always an in parameter passed by reference to the member method. Эта оптимизация позволяет избежать копирования при использовании readonly struct в качестве аргумента in . This optimization saves copying when you use a readonly struct as an in argument.

    Не следует передавать тип значения, допускающий значения NULL, в качестве аргумента in . You shouldn’t pass a nullable value type as an in argument. Тип Nullable не объявлен как структура только для чтения. The Nullable type isn’t declared as a read-only struct. Это означает, что компилятор должен создавать защитные копии для любого аргумента типа, допускающего значение NULL и передаваемого в метод с помощью модификатора in в объявлении параметра. That means the compiler must generate defensive copies for any nullable value type argument passed to a method using the in modifier on the parameter declaration.

    Вы видите пример программы, который демонстрирует разницу в производительности с помощью Benchmark.net в наших репозиториях примеров на сайте GitHub. You can see an example program that demonstrates the performance differences using Benchmark.net in our samples repository on GitHub. Он сравнивает передачу изменяемых структур по значению и по ссылке с передачей неизменяемых структур по значению и по ссылке. It compares passing a mutable struct by value and by reference with passing an immutable struct by value and by reference. Быстрее всего использовать неизменяемую структуру и передачу по ссылке. The use of the immutable struct and pass by reference is fastest.

    Используйте типы ref struct для работы с блоками или памятью в одном кадре стека Use ref struct types to work with blocks or memory on a single stack frame

    Еще одной связанной функцией языка является возможность объявлять тип значения, который должен быть ограничен одним кадром стека. A related language feature is the ability to declare a value type that must be constrained to a single stack frame. Это ограничение позволяет компилятору кое-что оптимизировать. This restriction enables the compiler to make several optimizations. Главным стимулом для создания этой функции была структура Span и связанные структуры. The primary motivation for this feature was Span and related structures. Вы получите повышение производительности благодаря этим усовершенствованиям, если будете использовать новые и обновленные интерфейсы API .NET, которые используют тип Span . You’ll achieve performance improvements from these enhancements by using new and updated .NET APIs that make use of the Span type.

    Похожие требования могут иметь место при работе с памятью, созданной с помощью stackalloc , или при использовании памяти из API взаимодействия. You may have similar requirements working with memory created using stackalloc or when using memory from interop APIs. Для этих задач можно определить собственные типы ref struct . You can define your own ref struct types for those needs.

    Тип readonly ref struct readonly ref struct type

    Объявление структуры как readonly ref сочетает в себе преимущества и недостатки объявлений ref struct и readonly struct . Declaring a struct as readonly ref combines the benefits and restrictions of ref struct and readonly struct declarations. Объем памяти, используемой диапазоном только для чтения, будет ограничен одним кадром стека, а объем памяти, используемой диапазоном только для чтения, невозможно изменить. The memory used by the readonly span is restricted to a single stack frame, and the memory used by the readonly span can’t be modified.

    Выводы Conclusions

    Использование типов значений сводит к минимуму число операций распределения: Using value types minimizes the number of allocation operations:

    • Хранилище для типов значений выделяется в стеке для локальных переменных и аргументов метода. Storage for value types is stack allocated for local variables and method arguments.
    • Хранилище для типов значений, которые являются членами других объектов, выделяется как часть этого объекта, а не отдельное распределение. Storage for value types that are members of other objects is allocated as part of that object, not as a separate allocation.
    • Хранилища возвращаемых значений типов значений выделяется в стеке. Storage for value type return values is stack allocated.

    Сравните это со ссылочными типами в таких же ситуациях: Contrast that with reference types in those same situations:

    • Хранилище для ссылочных типов выделяется в куче для локальных переменных и аргументов метода. Storage for reference types are heap allocated for local variables and method arguments. Ссылка хранится в стеке. The reference is stored on the stack.
    • Хранилище для ссылочных типов, которые являются членами других объектов, выделяется в куче отдельно. Storage for reference types that are members of other objects are separately allocated on the heap. Объект хранит ссылку. The containing object stores the reference.
    • Хранилище возвращаемых значений ссылочного типа выделяется в куче. Storage for reference type return values is heap allocated. Ссылка на это хранилище хранится в стеке. The reference to that storage is stored on the stack.

    Чтобы свести распределения к минимуму, придется пойти на компромисс. Minimizing allocations comes with tradeoffs. Вы копируете больше памяти, если размер struct больше, чем размер ссылки. You copy more memory when the size of the struct is larger than the size of a reference. Ссылка обычно является 64- или 32-разрядной и зависит от ЦП целевого компьютера. A reference is typically 64 bits or 32 bits, and depends on the target machine CPU.

    Эти компромиссы обычно имеют минимальное влияние на производительность. These tradeoffs generally have minimal performance impact. Однако для больших структур или больших коллекций влияние на производительность возрастает. However, for large structs or larger collections, the performance impact increases. Влияние может быть большим в плотных циклах и часто используемых путях для программ. The impact can be large in tight loops and hot paths for programs.

    Эти усовершенствования языка C# предназначены для критических алгоритмов производительности, когда минимизация распределений памяти может иметь большое значение для достижения необходимой производительности. These enhancements to the C# language are designed for performance critical algorithms where minimizing memory allocations is a major factor in achieving the necessary performance. Может оказаться, что в создаваемом коде эти функции используются довольно редко. You may find that you don’t often use these features in the code you write. Тем не менее эти усовершенствования были реализованы в .NET. However, these enhancements have been adopted throughout .NET. Поскольку с этими функциями работает все больше API-интерфейсов, повышение производительности приложений не останется незаметным. As more and more APIs make use of these features, you’ll see the performance of your applications improve.

    Основы программирования: как начать писать код

    Окончил курс по языку программирования, но писать код не научился. С подобным сталкиваются многие новички. Пользователи Reddit описывают проблему так:

    • Я прошёл онлайн‑курс по Python, но всё равно не знаю, как написать полноценную программу.
    • Я знаю теорию, но не могу применить её на практике.
    • Я знаю, что такое цикл while, но не знаю, как и в каких случаях использовать его.

    Разбираемся, в чём может быть проблема и как её решить.

    Проблема: искусственная среда программирования

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

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

    Проблема: чрезмерные руководства

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

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

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

    Синтаксис — это просто набор символов, которые используются для определённого языка программирования. Можно провести параллель с естественными языками: умение написать и произнести фразу на французском “S’il vous plaît” не имеет смысла, если вы не знаете её значения.

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

    Решение 1: использовать реальные среды разработки

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

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

    Напишите код, сохраните его и запустите — это будет вашим первым серьёзным шагом, ведь именно так работают настоящие разработчики.

    Решение 2: писать код с нуля

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

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

    Не копипастите чужой код. Если вы используете работающих чужой код — вы ничему не научитесь. Изучайте чужой код, но не копируйте!

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

    Решение 3: писать много кода, очень много кода

    Программирование — не теоретическая дисциплина: чтения книг, просмотра учебных видео и выполнения тренировочных упражнений недостаточно, чтобы освоить её. Чтобы научить программировать, нужно написать тысячи строк кода.

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

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

    Решение 4: просить о помощи

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

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

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

    1. Скопируйте сообщение об ошибке, которое выводится в редакторе и укажите его в вопросе.
    2. Нет сообщения об ошибке, объясните, какого результата вы ожидаете от работы программы, и что происходит при её запуске на самом деле.
    3. Вставьте фрагмент кода, укажите код полностью в посте, если он небольшой. Если большой — используйте Github Gist или Pastebin и укажите ссылку на код.
    4. Отформатируйте код. Не вставляйте его обычным текстом, используйте редактор кода.
    5. Напишите, что вы уже пытались сделать с кодом.
    6. Используйте корректную терминологию — в этом вам поможет изучение теории.

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

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

    Как научиться писать самостоятельно код?

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

    Но само решение придумывает программист, а не язык программирования.

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

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

    В конкретно Вашем случае:
    Сделайте себе такую табличку:
    Название функции => выполняемое действие => Общий вид => Пример(ы) вызова
    Затем рассортируйте по тематикам (сгруппируйте).
    Как появляется вопрос — сразу смотрите в табличку, находите нужную и применяете.

    Такое можно делать даже тем, кто очень хорошо помнит только один язык, но работает сразу с несколькими: это помогает «сводить концы с концами» (в плане подобия функций в разных языках, если что-то нужно подобное сделать на другом).
    Язык программирования => Название функции => выполняемое действие => Общий вид => Пример(ы) вызова

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

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

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

    Так же очень полезный навык — декомпозиция. Слона или кита нужно есть частями. Другими словами большую задачу дробим на логически обособленные части, сами части дробим еще, до тех пор, пока каждую маленькую часть не сможем решить кодом размером в 1-2 экрана. Тщательно тестируем на всякие редкие и крайние ситуации. Оформляем этот код в виде модулей, далее собираем как из кубиков «Лего» нужный результат. Тестируем связки компонент, на моем опыте примерно половина времени уходит на продумывание и гугление, от оставшегося времени 90% уходит на дебаггинг и тестирование, и только примерно 5% совокупного времени реально идет на собственно кодинг.

    программист делая программы решает задачу
    задачу ставит заказчик или сам програмист

    поставь себе интересную задачу и пиши код несколько лет как дипломный проект
    например: чат блог форум редактор игру .

    Если серьезно, вам уже выше написали:
    1 практика — берите заказы, больше заказов, разных
    2 учите алгоритмы — посмотрите, по каким книжкам обучают сейчас в вузах. прочитайте хотя б одну
    3 основная масса задач — типовые. для них инженерные решения вроде подбора алгоритмов не нужны. старайтесь искать не типовые задачи (хоть те же олимпиадные задачки попробуйте порешать).

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

    Правила верстки

    Правила написания HTML

    На данный момент большинство браузеров отображают сайты, самостоятельно расшифровывая и подправляя ошибки веб-мастеров. Однако нужно быть внимательным при написании HTML – необходимо следовать правилам валидности, т.к. правильная верстка важна для оптимизации сайта, а также поможет не сойти с ума пользователям, которые откроют ваш сайт в браузерах ранних версий.

    Используйте

    Элемент располагается на первой строке любой HTML-страницы. Он определяет версию языка разметки, который используется на странице. На данный момент рекомендуется использовать doctype вида – он является универсальным для любой версии языка.

    Используйте правильную структуру документа

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

    Продвинутые правила и техники написания кода

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

    Что такое рефакторинг и зачем он нужен?

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

    Никогда не перерабатывайте код готового приложения без проведения тестов

    Мой первый совет — никогда не начинайте перерабатывать унаследованный код, который не проходил модульные тесты. Думаю, причина очевидна: многие функциональные элементы могут оказаться поломанными, и их будет трудно починить, потому что вы не сможете понять, в чем именно дело. Если вам нужно перепроектировать код, сначала протестируйте его и убедитесь, что сделали это с той частью, с которой будете работать.

    Начинайте рефакторинг с самой глубокой части кода

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

    Как вы можете заметить, на картинке красным цветом отмечены три уровня. Самая глубокая его часть — конструкция if/else, что внутри первого if. Лучше всего сконцентрироваться на одном логическом элементе, тогда будет проще переработать весь код.

    Сокращайте методы, разделяя их на более короткие или на конфигурационные файлы/DB-таблицы

    Может быть, в этом случае можно выделить эту часть кода в приватный метод:

    Следующий шаг — получить данные и загрузить их отображение. Переработав остальные части, взгляните на метод add(). Он стал гораздо «чище», его легче читать и тестировать.

    Всегда используйте <> в функции if

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

    Не используйте магические числа и строки

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

    Чтобы переработать это место, можно предположить, что 250 — максимальное количество комнат. Вместо хардкода выделим это в переменную $maxAvailableRooms . Теперь другие разработчики смогут понять это место.

    Не используйте оператор else, если того не требуется

    В этой же функции availableRooms() можно заметить функцию if, в которой можно запросто убрать часть else без вреда логике.

    Используйте понятные названия для методов, переменных, тестов

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

    Используйте максимум возможностей вашего языка программирования

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

    Илон Маск рекомендует:  Как постpоить лабиpинт
Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL