среда, мая 21, 2008

Хороший стиль Flex-программирования. Именование

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

Adobe любит нас, и создал для нас небольшой свод правил, который поможет не только лучше понимать Flex SDK, но и нам самим сделать свой код лучше:
Flex SDK coding conventions and best practices

Итак, сегодня мы поговорим об именовании (Naming).

Именование

Наши стандарты именования соответствуют стандартам ECMAScript и Flash Player 9.


Использование сокращений

Вообще, лучше их избегать. Например:
можно: calculateOptimalValue(), нельзя: calcOptVal().

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

Однако, есть общепринятые сокращения:
acc = accessibility (ButtonAccImpl)
auto = automatic (autoLayout)
eval = evaluate (EvalBindingResponder)
impl = implementation (ButtonAccImpl)
info = information (GridRowInfo)
num = number (numChildren)
min = minimum (minWidth)
max = maximum (maxHeight)
nav = navigation (NavBar)
regexp = regular expression (RegExpValidator)
util = utility (StringUtil)

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

Иногда можно встретить отсутствие логики в применении сокращений. Например, horizontalScrollPolicy и verticalScrollPolicy, но HBox и VBox.


Использование аббревиатур

Аббревиатуры довольно часто используются во Flex: AIR, CSS, HLOC, IME, MX, MXML, RPC, RSL, SWF, UI, UID, URL, WSDL, XML.

Аббревиатуры всегда пишутся либо заглавными, либо строчными буквами:
можно: SWF, swf, нельзя: Swf

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

Например:
можно: CSSStyleDeclaration, IUID, uid, IIME, imeMode.


Разделение слов

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

В некоторых местах может встретиться нарушение этого правила: комбинация слов становится целым словом: dropdown, popUp, pulldown.


Имена, содержащие тип

Если в имени нужно указать тип, поместите его, не сокращая, в конец имени. Откажитесь от использования суффиксов типа "_mc" по старой традиции ActionScript 1.

Например:
можно: border, borderSkin, or borderShape, нельзя: border_mc

Часто лучшим именем для объекта является имя его типа в отличающемся регистре (не соглашусь, т.к. смысловой нагрузки никакой - годится только для тестов и примеров):
var button:Button = new Button();


Имена пакетов

Начинаются со строчной буквы и для разделения слов используется регистр: controls, listClasses.

Имена пакетов должны быть всегда существительными или отглагольными -ing формами (герундий), но не глаголами, прилагательными или наречиями.

Пакет, включающий множество схожих элементов должен именоваться общим названием этих элементов во множественной форме: charts, collections, containers, controls, effects, events ...

Обычно, -ing формами именуются пакеты, реализующие концепции (я бы сказал процессы): binding, logging, messaging, printing ...
С другой стороны, это могут быть существительные, выражающие концепцию: accessibility, core, graphics, rpc ...

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


Имена файлов

Имена файлов импортируемых API должны соответствовать определениям public API. Но для include-файлов (файлов фрагментов кода) это правило не применяется.

Имя include-файла начинается с заглавной буквы и разделение слов в имени осуществляется регистром: BorderStyles.as, ModalTransparencyStyles.as

Имена файлов ресурсов пишутся строчными буквами, а слова разделяются символом подчеркивания: icon_align_left.png


Имена пространств имен

Именуются строчными буквами с разделением слов символом подчеркивания: mx_internal, object_proxy.


Имена интерфейсов

Начинаются с заглавной "I", разделение слов в имени осуществляется регистром: IList, IFocusManager, IUID.


Имена классов

Начинаются с заглавной буквы, разделение слов в имени осуществляется регистром: Button, FocusManager, UIComponent.

Подклассы Event именуются FooBarEvent.
Подклассы Error именуются FooBarError.
Подкласс EffectInstance, ассоциированный с эффектом компонента FooBar именуется FooBarInstance.
Подклассы Formatter именуются FooBarFormatter.
Подклассы Validator именуются FooBarValidator.
Классы скинирования компонентов именуются FooBarBackground, FooBarBorder, FooBarSkin, FooBarIcon, FooBarIndicator, FooBarSeparator, FooBarCursor, и т.д.
Классы утилит именуются FooBarUtil (не FooBarUtils - пакет может именоваться во множественном числе, но класс - только в единственном).
Общепринято базовый класс именовать FooBarBase: ComboBase, DateBase, DataGridBase, ListBase.


Имена событий

Начинаются со строчной буквы, разделение слов в имени осуществляется регистром: move, creationComplete.


Имена стилей

Начинаются со строчной буквы, разделение слов в имени осуществляется регистром: color, fontSize.


Значения строковых свойств

Начинаются со строчной буквы, разделение слов в имени осуществляется регистром: "auto", "filesOnly".


Имена констант

Именуются полностью заглавными буквами с разделением символом подчеркивания: OFF, DEFAULT_WIDTH.

Если константа строковая, слова в имени константы должны соответствовать словам в строке ее значения:
public static const FOO_BAR:String = "fooBar";


Имена свойств (переменные и установщики)

Начинаются со строчной буквы, разделение слов в имени осуществляется регистром: i, width, numChildren.

Используйте "i" для имени переменной цикла "for", и "n" для имени верхнего предела цикла.
Используйте "j" для имени переменной вложенного цикла "for", и "m" для его имени верхнего предела цикла:
for (var i:int = 0; i < n; i++)
{
for (var j:int = 0; j < m; j++)
{
...
}
}

Используйте "p" для имени переменной цикла "for..in":
for (var p:String in o)
{
...
}

Бывают ситуации, когда в классе необходимо переопределить установщик, но предполагается, что базовый установщик будет продолжать использования. Для сохранения базового установщика, необходимо создать новый установщик, имя которого должно формироваться путем добавления к имени базового установщика символа "$". Полученный установщик нужно определить как "final". Он не должен ничего делать, кроме вызова базового установщика. Напимер, нам нужно перекрыть установщик numChildren, но при этом сохранить возможность вызывать базовый super.numChildren - для последнего мы создаем следующее:
mx_internal final function get $numChildren():int {
return super.numChildren;
}


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

К имени установщика добавляется символ подчеркивания "_":
для foo имя переменной _foo.


Имена методов

Начинаются со строчной буквы, разделение слов в имени осуществляется регистром: measure(), updateDisplayList().

Имена методов должны быть всегда глаголами.

Лучше не использовать методы без параметров подобно getFooBar() или setFooBar(). Вместо них используйте установщики. Однако, если getFooBar() - медленный метод, требующий больших вычислений, лучше его назвать findFooBar(), calculateFooBar(), determineFooBar(), и т.д., выражая его суть, а не то, что он способен возвращать что-либо.

Аналогично установщикам, если базовый метод перекрывается, но требуется сохраненить доступ к нему, создается метод с именем базового метода, перед которым добавляется символ "$":
mx_internal final function $addChild(child:DisplayObject):DisplayObject
{
return super.addChild(child);
}


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

Имя обработчика события формируется путем добавления к имени типа события слова "Handler": mouseDownHandler().

Если обработчик предназначен для событий, отправленных субкомпонентами (т.е. не this), перед именем обработчика, сформированного по вышеописанному правилу, добавляется имя субкомпонента, отделенное символом подчеркивания "_": textInput_focusInHandler().


Имена аргументов

Всегда используйте для установщиков имя аргумента "value":
можно - public function set label(value:String):void
нельзя - public function set label(lab:String):void
нельзя - public function set label(labelValue:String):void
нельзя - public function set label(val:String):void

Всегда используйте для обработчиков событий имя аргумента "event":
можно - protected function mouseDownHandler(event:Event):void
нельзя - protected function mouseDownHandler(evt:Event):void


Имена пакетов ресурсов

Если пакет рессурсов содержит ресурсы для определенного пакета классов, имя пакета рессурсов должно быть таким же: controls, {formatters}}, validators.


Имена идентификаторов ресурсов

Начинаются со строчной буквы, разделение слов в имени осуществляется регистром: pm, dayNamesShort.


Прочая терминология

Избегайте использование слова "object", т.к. оно вносит неопределенность.

Слово "item" - элемент данных и никогда DisplayObject.

Слово "renderer" - это DisplayObject, который отображает элемент данных.

Слово "type" означает тип AS3, используйте вместо него слово "kind".

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

Следующий раздел "Language Usage" разберем в ближайшее время.

11 комментариев:

Антон Волков комментирует...

Отличная работа! Спасибо

Mike комментирует...

Не лучше ли вместо "установщик" использовать "сеттер"?

Racer комментирует...

Я использовал принятый в литературе термин "установщик/получатель" (кроме того, я опустил слово "получатель" - на мой взгляд, "установщик" вполне отражает сущность термина, когда нет конкретики). Мне кажется это правильно :). Да и слово красивое.
В разговорной же речи, я за геттер/сеттер, т.к. это удобнее.
Кстати, Сеттер - это порода собак.

Q-Zma комментирует...

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

Racer комментирует...

Как упоминалось выше, эти правила включают большинство правил ECMAScript, а JS также входит в число диалектов ECMAScript: http://en.wikipedia.org/wiki/ECMAScript.

Любой код, AS или JS, оформленный согласно каким-либо правилам, выглядит красиво и профессионально. Чем ближе правила оформления к стандарту, тем больше людей сможет в нем разобраться. Тем легче отлаживать, производить рефакторинг, генерировать документацию и т.д.

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

Q-Zma комментирует...

Обычно обзываю переменные в JS очень коротко, чтобы уменьшить размер исходника. Конечно, удобнее и понятнее писать myVariableName чем mvn, но привычка, блин, со времён медленного интернета :)

Racer комментирует...

Да, тема снижения объема сокращением длинных имен вполне может быть актуальна для JS. А для сильнопосещаемых сервисов даже очень.

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

Кстати, столкнулся с похожей ситуацией, когда програмил под SmartFox Server. При формировании данных для обмена с сервером как раз очень важен размер переменных. Там переменные складываются в объекты, которые затем преобразовываются в пакет и отправляются на сервер/клиент.
Для циклически повторяющихся запросов, сокращение имен жестко актуально, т.к. на этом можно сэкономить мегабайты трафика.

lembrd комментирует...

"нельзя - protected function mouseDownHandler(evt:Event):void
можно - protected function mouseDownHandler(event:Event):void"

Почему ?

Racer комментирует...

Потому что "Всегда используйте для обработчиков событий имя аргумента event".
Это так же следствие правила "Использование сокращений".

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

lembrd комментирует...

Я не совсем точно поставил вопрос. Он должен был звучать так: "Почему `всегда используйте для обработчиков событий имя аргумента event`?" Типичный обработчик событий принимает всегда только один аргумент, и только из иерархии flash.events.Event, поэтому рекомендацию не сокращать имя этого аргумента считаю безосновательным, а на против советовал бы использовать краткую форму handler(e:SomeEvent), как скажем в случае с простыми, не вложенными, итерирующими по индексам циклами for (i;i<10;i++)... А к написанию этого коммента сподвиг тот факт, что я хочу этот пост использовать как "обязательные к исполнению стандарты" внутри коллектива разработчиков.

Racer комментирует...

Ок, идею понял.
На мой взгляд разница между i и e существенна.

Переменная цикла i - простая целочисленная переменная (думаю, что это сокращение index), и разработчики используют ее с древних времен (я впервые ее применил где-то в начале 90х в фортране на лабах по каким-то основам программирования). То есть это в принципе многолетняя традиция. Кроме того, есть еще вложенные циклы с j, k, ...

Аргумент обработчика события event - это объект. Имеет набор свойств, методов и т.п. Мне кажется, что при обращении к ним, большое значение имеет как это выглядит и читается. Например, event.type впринципе так и прочитается тип события (ну по русски то как всегда всё шиворот на выворот :) а по их - всё четко). И сравним прочтение с e.type.
Про сокращения evt я вообще молчу, - где смысл в сокращении всего на 2 буквы?

И дело вообще не просто в обработчике событий, а в общей системе - полнота и читабельность.